首发于酷家乐前端博客,做者@摘星(segmentfault @StinsonZhao)javascript
咱们能从不少地方学习到怎么起一个 Electron 项目,有些还会介绍怎么打包或构建你的代码,但距离「真正地发行一款 Electron 产品」这一目标,还有不少工做须要作...css
这是 Electron 系列文章的第二篇,这一篇文章将和你们分享我是怎么去构建自动化的 Electron 开发构建工程的,说白了,就是怎么把敲的代码变成一个用户能够下载安装的包,固然随着以后应用复杂度的提高和技术再选型,工程体系可能随时会重构或演进,但至少能够给你们一些参考,欢迎留言交流。html
这是一篇很长的文章(手册),写得比较「唐僧」(知我者能够说我写得比较用心),至少会花你一天时间(没开玩笑),适用场景是「用 Electron 打造 Windows 或 Mac 应用」,是的,你没看错,同时会讲清楚兼容 Win 和 Mac 两个系统的流程。文中说起的技术方案绝对不是最佳的(我保证),由于几乎每隔几天我都会发现某个环节能够作得更好,但要明白要唱多大的戏,就先搭多大的台,够用就好,不要为了搭台耽误演出时间。前端
工程自动化,应该是全部开发者的一种基础追求,当你搭建建好工程体系,之后你将专一于产品功能的开发,而不会花大量没必要要的时间去手动构建。做为前端,可能咱们已经熟悉了 web 应用的构建和部署,可是客户端程序有其自己的特色,相比较 web 应用最大也是我认为最根本的一点区别在于「你的应用是被用户下载过去安装在用户本地再跑起来的」。java
这一区别对工程的影响在于,你不可能把你的代码部署到「用户的电脑」,你须要构建安装包,你须要针对不一样的用户系统构建不一样的安装包,你须要让你的应用被系统认为是安全的... node
本文须要作的是,把客户端的打包构建发行这一流程作到像「把大象放进冰箱」同样的简单:打开命令行,敲一个 npm run xxx
,喝一口咖啡,咪哩嘛哩哄,安装包出现(一开始打造这个流程时,剧本多是「喝一口咖啡,啪,Error 了,又 Error 了」,take it easy,生活须要慢慢品味 —— 来自一位25岁的仙风道骨白胡子程序员)。jquery
本文将分如下小节和你们分享「从本地的代码到云端可下载的安装包」这一路的风景,你会有漫步月球般的感受(由于月球全是坑啊,还没氧气):linux
下面一一展开进行阐述,再次强调,文中不少依赖的技术或包,你均可以尝试替换成本身相中的,没必要在乎是选「翠花」仍是「桂花」,多到处就知道了。git
如下目录结构供参考,没有很详细地展开,由于每一个应用可能不一样,最想表达的是这是一个「双 package.json 结构」,你能够看到根目录下有一个package.json
,app
目录下还有一个package.json
。程序员
/ // 项目根目录 ├── app/ // 应用源码目录,打包就针对app目录进行打包 │ ├── assets/ // 应用须要的图片、icon等资源 │ ├── config/ // 配置文件存放 │ ├── consts/ // 应用运行须要的常量,如ipcChanel │ ├── lib/ // 引入的库文件,如jquery │ ├── plugins/ // 应用运行须要的插件,如flash │ ├── utils/ // 经常使用的工具方法 │ ├── view/ // 视图,html、css和js │ ├── app_config.js // 整个app的配置,引入config文件夹下文件 │ ├── main.js // 应用入口 │ ├── package.json // 内部的package,定义应用的版本、运行依赖等 │ └── yarn.lock ├── build_resource/ // 构建须要的一些工具、资源或者脚本的目录 ├── config/ // 环境配置文件目录,会选择一个写入到app/config ├── deploy/ // 部署脚本,用户部署文件到cdn或上传文件到OSS ├── reserve/ // 保留目录,存放一些文件用于写入到app内 ├── dist/ // 打包和构建的目标目录 ├── release/ // 发行的目标目录 ├── .gitignore ├── gulpfile.js // gulp配置 ├── package.json // 外部package.json,用于定义开发依赖和脚本 └── yarn.lock
这是由于,咱们的应用在运行时须要一些第三方依赖,这些依赖咱们须要打包到应用内,也就是说/app/node_modules
目录内的内容是要被打包到应用内的,用户使用的时候才不会缺失「运行时依赖」,而若是咱们只有一个package.json
,那么全部的依赖都被下载和安装到同一个node_modules
文件夹下,咱们无法把咱们须要打包进去的依赖树提取出来。因此这样双 package.json
的结构最清晰明了和简单易用,dependencies
和devDependencies
有了明确划分。
再大体解释下其余目录的做用:
app
目录:是咱们应用的源码目录,咱们所说的打包针对的就是这个目录,其余目录和文件不会被打包进去,而app
目录内的子目录和文件就见仁见智了,在不一样的复杂度下有不一样的设置,这里还有一些东西是须要从外面复制进来的,由于不一样的平台下你可能须要打包进去的东西是不一样的。
config
:配置文件目录,可能由于你想打包的应用所处的阶段(开发、内测、众测、正式发行)和平台(Windows、Mac),那么可能须要不一样的配置,好比一些资源的名称和路径等,这里你能够把不一样状况下都同样的配置写到一个配置文件,而根据状况不同的配置文件是从外部脚本写进来的,这就是为何你会在app
目录外面看到一个config文件夹的缘由plugins
:是插件文件夹,你可能须要给本身的应用加一些插件,好比 flash,而一个 flash 插件有 40M 左右,Win(32bit)、Win(64bit) 和 Mac 须要的 flash 插件文件都是不同的,因此若是所有打包进你的应用,再用「if - else」去选显然是不科学的,Mac 下的应用确定是用不到 Win 版本的插件的,因此这里的文件也是从外面脚本写进来的view
:是视图文件夹,也能够说是渲染进程对应的代码文件夹build_resource
:构建资源或工具文件夹,这个文件夹下放打包到发行这一流程中须要用到的资源和工具,好比程序主图标、构建安装包的配置脚本(win)、代码签名工具等deploy
:存放部署脚本的文件夹,这里的脚本负责把你的应用安装包上传到云存储(OSS),咱们会在 gulp 中的发行环节引入这里写的脚本进行自动上传安装包dist
和release
:前者是打包和构建安装包这两步的 output 目录,后者是最终咱们会上传到云端的安装包目录,构建和发行环节的差异咱们后面会讲到这个部分无法正向推导,我是从一个乱七八糟的 windows 开发流程开始的,而后修改为一个合适的 windows 开发流程,再由于要兼容 Mac 的开发,再改为如今这样的流程设计的,因此我无法从一开始就说由于什么因此要考虑什么,而后慢慢构建出一个合适的工做流,这是上帝视角,这个偏实践经验的过程必定是实践越多,感觉越多的。
因此我会先说个人作法,再说这么作的好处,所用的工具是 gulp(若是不熟悉,能够去 gulp 官网看一下,很容易上手),利用 gulp 的 task 串起整条流程,我把工程中的一个阶段称为一个环节,是为了和应用自己的阶段(开发、内测、众测或正式发行)作一个区分,否则都不知道说的阶段是指啥:
.exe
和 Mac下的 .dmg
,而且对这两个安装包也须要代码签名,这一步后你的应用能够被分发安装啦以上每一步,Mac 版和 Windows 版的开发都须要经历,只是所用的方法不一样,这样作的好处,一是统一了 Mac 和 Win 下开发工做流的生命周期,二是简单和直观,每一环节目的是什么,输出是什么很明确。
如此,我在package.json
中的script
就能够这么写:
... ... "start": "cross-env NODE_ENV=dev gulp dev", "packDev": "cross-env NODE_ENV=dev gulp pack", "buildDev": "cross-env NODE_ENV=dev gulp build", "releaseDev": "cross-env NODE_ENV=dev gulp release", ... ...
固然这里的NODE_ENV
你也能够写成命令行参数(我只是习惯了用这个),利用这个参数去指定须要针对的应用阶段,像以上这样就配好了「dev」阶段的相关脚本,能够用npm run packDev -- --platform="xxxx" --arch="xxxx" --sign
这样形式的命令行去执行不一样的 gulp 任务,后面的参数,是须要咱们在 gulpfile 文件中解析的,以上3个参数分别表示「系统平台」、「系统位数」、「是否须要代码签名」,咱们能够在 gulpfile 文件中给这些参数合适的默认值,使操做更人性化。
目的:一是为以后的环节初始化工做流参数,二是准备好应用文件夹内容(即要打包的目标文件夹 —— app)
作的事:解析命令行参数,初始化工做参数,填充配置文件,把配置文件和相关依赖文件导入到app
文件夹内合适的地方
所用工具:yargs
yargs 是一款优秀的命令行参数解析工具,咱们要初始化的工做参数包括如下 3 个:「系统平台」、「系统位数」、「需不须要签名」,你也能够把应用的所处阶段(开发、内测、众测、正式)设计成参数。
// 如下 3 个变量在 gulpfile 内全局声明 // 这里的 detectPlatform() 须要本身写,利用 node 的 os 模块去检测开发机环境从而给出 // 为了理解上直观一些,把 32 位的 win 写成 win32,64 位的 win 写成 win64 // node os.platform() 没有 win64 的返回的,只有在返回 win32 基础上,你再使用 os.arch() 去肯定是不是win64 // 可能的合法值:darwin、win6四、win32 platform = yargs.argv.platform || detectPlatform() || 'win32'; // 系统位数,若是是 Mac OS X,不考虑 32位 // 可能的合法值:x6四、ia32 arch = platform === 'darwin' ? 'x64' : (yargs.argv.arch || 'ia32'); // 布尔值,指定是否须要代码签名 needSign = yargs.argv.sign || process.env.NODE_ENV === 'prod' || platform === 'darwin';
看到上面的参数初始化,可能会有疑问,既然已经在platform
中区分了 win32(32bit) 和 win64(64bit),并且darwin
下不考虑 32bit(由于 OS X 10.6 以后就全是 64 位的),arch
参数是否多余?这是能够认为是多余的,可是有的话更完整,并且若是你之后又想兼容 linux 了呢?
所用工具:gulp API、gulp-replace、gulp-rename
首先我会在根目录下的 config 文件夹下放几个不一样的配置文件模板,分别对应应用不一样的阶段的配置(好比dev.js、alpha.js、beta.js、prod.js),而后利用gulp-replace
去替换掉里面的一些占位字符串(也就是填充模板),最后利用gulp-rename
重命名为好比env.js
后,利用gulp.dest
写入文件到 app/config 目录下,因而配置文件 Ready。
所用工具:gulp API、del
以 flash 插件为例,首先你要找到须要的插件文件,electron 官网所说的打开chrome://plugins
已经无法用了,从 chrome 的某个版本开始,chrome://plugins
Is Not Available。
因此用系统的搜索功能吧,记得先装下 chrome 浏览器,Mac 搜索「PepperFlashPlayer.plugin」,Windows 搜索「pepflashplayer」,Windows下若是搜到多个,记得选择和 chrome 目录有关的那个「.dll」文件,此外 win32bit 和 win64bit 所用的 flash 也是不一样的,Mac 下的「PepperFlashPlayer.plugin」本质是一个文件夹,整个文件夹都须要。全部的3个插件放进根目录下 reserve 文件夹。
接下来须要作的就是,根据不一样的平台读不一样的 flash 插件( .dll 文件或 .plugin 文件夹)到 app/plugin 文件夹下。
这里有一个须要注意的是,每次你构建时,若是 app/plugin 下的 flash 不是你要的,那么你须要先删除那个旧的,不然你的 app/plugin 文件夹下会躺着一个你不会用的 flash 插件,但会被打包进去,你的文件大小忽然多了 40M,我这里用的删除工具是 del。
通过配置环节,app
文件夹已经准备就绪,因此以开发模式(不须要打包)运行应用也就没啥大问题,能够另写一个「dev」的 gulp task,利用 node 的child_process
模块下的exec
调用下electron app --debug
就能够运行应用了,没啥能够多说的,咱们继续进入下一步 —— 打包。
目的:产出一个可执行程序,简单来讲,就是能有一个应用,双击能运行起来
作的事:利用electron-packager
打包,补充应用信息(only for win)
electron-packager
打包利用electron-packager
打包,只须要针对不一样系统平台给出不一样的配置,而后调用其 API 就能够了。
// Mac const options = { dir: './app', name: '应用名字', platform: 'darwin', arch: arch, // 这就是工做参数 arch overwrite: true, appVersion: 'Copyright(C) 2017 Qunhe', asar: { unpackDir: 'plugins' // plugins 内的文件咱们不但愿打进 asar 格式包内 }, out: './dist', icon: './build_resource/logo.icns' // Mac 下 icon 格式是 .icns }; // Win const options = { dir: './app', platform: 'win32', // 无论是 32bit 仍是 64bit 的 win,这里都是 win32 arch: arch, // 这里依靠 x64 或 ia32 去区分位数 overwrite: true, asar: { unpackDir: 'plugins' }, out: './dist', icon: './build_resource/logo.ico' // Win 下 icon 格式是 .ico };
Mac 下各处(Dock、任务栏、进程名等地)展现的应用名字只要指定了name
选项,就是到处同样的,因此你能够用 name 指定一个中午名字,并且 Mac 下默认编码都是 UTF-8,问题不大。
而对于 Windows,首先其中文默认编码是 GBK 的,而因此若是指定中文名字可能会有奇怪的问题,因此 Windows 应用通常我不填name
项,这样它会去找你 app 目录下的 package.json 文件中的productName
或name
字段值,这个字段通常设置是英文的,第二个不去设置中文的缘由是,Windows 下应用的展现名字是 exe 主程序的FileDescription
配置项决定的,若是不去设置,那么可能你的应用用任务管理器打开,显示的进程是「Electron」,而不是你的应用名字。
关于应用的实际名字和展现名字,Win 和 Mac 下都有本身的一套,这里不细展开。而基于目前的实践,我给的建议是,Mac 下的开发,你能够直接指定name
为一个你要的中文应用名,而对于 Win,你最好像下面那样操做。
所用工具:rcedit
Command line tool to edit resources of exe file on Windows. 翻译过来就是一个用于编辑 exe 文件信息的windows 命令行工具,固然它已经有了 node 版本,叫 node-rcedit,也就是说你能够用 node 子进程的exec
去执行,也能够调用 node 版本的 API。
能够这么用:
execSync(` .\\node_modules\\rcedit\\bin\\rcedit // 调用rcedit ./dist/xxxxxx.exe // 目标文件(刚打包出来的主程序) --set-version-string "LegalCopyright" "Copyright(C) 2017 Health" // 版权信息 --set-version-string "CompanyName" "仙风道骨养生俱乐部" // 公司名字 --set-version-string "ProductName" "养生" // 产品名字 --set-version-string "FileDescription" "养生宝典" // 这个很重要,由于这个就是你打开任务管理器看到的进程名字 `);
大部分信息,你能够右键主程序(.exe)文件,「属性 —— 详细信息」中看到,这么作还有一个考虑是,这样你的应用看上去会更加规范。
这里确定有人说,为何不用electron-builder
,由于我首先接触到的是electron-packager
,我以为够用(由于我有一台 win 和一台 mac,跨平台打包,不存在的),第二,electron-packager
完成打包的事就够了,后面构建安装包等过程可让咱们有更多的选择,符合本文的工做流设定,每一个环节作每一个环节该作的事就好,固然你也能够选择electron-builder
,能达到目的就好。
目的:使应用被系统所承认,能正常安装
作的事:给应用进行代码签名
代码签名的目的就是为了安全,你的应用一旦通过了代码签名,若是发行过程当中被篡改,你的用户会看到系统给出的警告提示,而对于发行方而言,代码签名后,应用才能被系统承认,很大几率不会被杀毒软件作掉,并且若是你要提交一些软件市场,一些软件市场要求应用须要有合法的代码签名。
而若是做为铁头娃的你铁定不签名,这应用就不能跑了么?不是的,仍是能够跑的,只不过对你的用户来讲很不友好。
Windows 下代码签名的限制没有 Mac 那么严,你选择「是」都是能够安装使用的,可是从你产品的用户角度,有一个代码签名会更可靠,此外,这样的没有签名的安装包在一些软件市场可能都提交不上去。
Mac 下有和没有代码签名的差异就很大了,没有合法的代码签名,你的 .dmg 安装包根本无法打开。
若是没有代码签名,Mac 下的 .dmg 安装包打开,首先会提示你「该应用来自身份不明的开发者,是否确认打开」,而后你点「确认」,再根据你的安全设定(系统偏好设置 —— 安全和隐私 —— 容许从如下位置的应用下的设置)去决定,而绝大部分的 Mac 用户都是勾选「App Store 和 被认证的开发者」,因而就算你点了「打开」,直接会告诉你「打不开XXX,由于它来自身份不明的开发者」,这个时候只能去改变「系统偏好设置 —— 安全和隐私 —— 容许从如下位置的应用下的设置」才能打开。
典型的盗版软件安装方式啊,因此做为一款要发行的产品,咱们必定是须要代码签名的。
整体建议:我的的小项目就不用 Windows 代码签名了,由于很贵,2K+/年,并且 Windows 下代码签名没有问题不是很是大(和 Mac 相比),公司的产品,那就必需要的。
能够向权威的 CA 机构购买代码签名证书,这里就我了解的作一个建议:建议向赛门铁克购买签名普通软件(非驱动)的微软代码签名证书,大概几百刀一年。
背景说明:目前咱们用的是沃通的代码签名证书,赛门铁克的只是咨询过,没用过。
就以上的建议作一个解释,为何我这么建议:
当你购买了证书后,就能够利用signtool
命令行进行签名了,命令怎么写,这些都在你购买证书的 CA 网站上找到或者 google 一下,这里要说的就两点:
查看 Windows 代码签名信息很简单,右键你签名的文件,签名后的文件,属性打开会有一个「数字签名」的 tab,点击切换到「数字签名」能够看到代码签名信息。
整体建议:Mac 下应用要代码签名,由于很方便,也不是很贵,我的开发者 99 USD 一年,若是公司有 Apple Develop Team,你能够直接加入,关键是 Mac 下若是你不进行可供分发的代码签名,你的应用很难被他人安装啊。
证书是能够在 Xcode 下申请的,Xcode —— Preference —— Account 下,选择一个Team(以前要先加入),若是是独立开发者,就选本身 Apple ID 的那个,点击「Manage Certificates」,弹出的弹窗中左下角点加号,能够选择须要的证书。
我看到以后的第一反应是:尼玛,哪些是我要的啊。下面简单说明下(摘自Mac App 发布的最后 1km):
Developer Certificate
Production Certificate
Mac App Store
Developer ID
咱们主要须要的就是「Developer ID Application」这个类型的证书,「Mac Development」只是用于开发的,而前者能够供分发,也就是签名后,别人下载安装,就是来自「被认证的开发者」的应用啦。
若是是在一个 Team 中,不是我的独立开发者,那么这个「Developer ID Application」证书的申请你是没有权限的,就算大家 Team 的 Agent 设置你为 admin(管理员),你仍是没有权限的,由于一个「Developer ID Application」只有一个 Team 的 agent(owner) 才能申请,你须要作的是利用你 Mac 上的钥匙串工具(具体怎么作,google 下就能够了),生成「CertificateSigningRequest」(简称 CSR),而后发给你的 team agent,让他帮你生成证书,发回给你,你再安装到本身机子上,搞定。
你能够在终端调用security find-identity -p codesigning -v
来看一下你可用的代码签名证书,其中那个Developer ID Application
开头的就是咱们要的。
所用工具:electron-osx-sign
Mac 下的签名简直是红红火火开开心心嘿嘿哈哈啊,你能够从electron-osx-sign 指导这里得到彻底的指导,你在这个页面右边能够根据你的项目进行填写,页面最后会根据你的配置,给你一段你均可以直接复制的签名代码,完美。
并且签名还能集成到打包阶段,不过我建议仍是拿出来好,比较清真。
Mac 下查看文件签名信息,你能够终端运行codesign --display --verbose=4 "文件路径"
。
目的:使你的应用能够被安装(若是没有这一步,你能怎么办,压缩整个应用文件夹,而后分发这个压缩包,呃,你能接受也能够啊)
作的事:把经历了打包和签名环节后的应用程序文件夹(Mac 下的.app
其实也是文件夹)打成一个安装包文件
为何要构建安装包,这有不少的缘由,可能你也会想到不少,其中值得强调的两点,一是构建安装包会直接便利于应用的自动更新,具体咱们下一篇文章里再说,二是 Win 下安装包的体积相比原先的文件夹,体积明显小不少,在硬盘容积很大的时代,下载体积才是最影响用户体验的,而安装后的体积不是最须要考虑的体积。
安装包这个事和代码签名相似,两个不一样的系统(Win 和 Mac)实现彻底不一样,Windows 下咱们习惯.exe
或.msi
这样的安装包格式,习惯点下一步到完成或一键安装,而 Mac 下除了 Store 下载安装的,咱们习惯的.dmg
格式的,挂载后打开,将里面的应用拖入到Application
文件夹就完成了安装。
这里咱们实现的就是经典的 Windows exe 安装和 Mac dmg 安装,相比较而言,Windows 下的繁琐得多得多。
最终说服我使用 inno setup 来构建应用安装包的理由是,VS Code 也是这么作的。由于按照程序这个领域离一个小前端已经很遥远了,对于跨度大的未知东西,通常都会作充足的调研,最后发现 VS Code 也是这么作的,好,干!
而使用了一段时间后,我能够说几点不后悔的理由(固然我没使用过其余的安装包构建工具,因此仅一些偏见):
先能够本身去搜一下 inno setup,进入官网逛一逛,下载安装一下(记得安装 unicode 版本,即括号里有 u 的版本),浏览后有几个基本认知须要具有:
有了上面的几点认知,能够给出「学习和使用 inno setup 路径」的建议:
所用工具:修改后的 gulp-inno
若是按照以前的步骤花了个把小时大概学习了下 inno setup 的话,那么到这里你应该能够尝试把 inno setup 构建安装包作到你的 gulp 工做流中了,若是还不熟悉 inno setup 配置文件,不要紧,你能够从仿照开始,不要怂,就是干,都到这一步了,谁怂谁尴尬。
配置文件的详解不是这里的重点,因此再也不展开,把 inno setup 整合进脚本中,由于它自己提供命令行工具,勤快和好学的你能够根据官方或其余渠道的指导本身封装一个 node 模块,而我就比较懒了,搜到一个已有的 gulp 插件 —— 「gulp-inno」,高兴地一匹。
然而,事情总不会那么顺利,该吃的shi躲不掉,该经历的坑绕不过,这才叫「历shi」。我利用「gulp-inno」根据其指导怎么都不能正确编译,大概提示是有不合法的字符的意思。
明白了,绝壁是「gulp-inno」里包的 inno setup 不是 unicode 版本,因此一旦有中文等字符,就出错了,我看到这个包里的 inno 文件夹彻底就是和个人 inno setup 文件夹没差嘛,因而我把我本地安装的 inno setup 文件夹里内容复制替换到 gulp-inno 的 inno 的文件夹内,问题解决。
由于我以前导入过中文语言包,因此我复制过去的时候,中文语言包也复制过去了,能够愉快地配置安装向导界面为中文了。
一旦修改好「gulp-inno」包(替换成 unicode 版本 & 加入简体中文语言包),就能够怎么操做:
// 1. 准备 iss 文件:填充你的 iss 配置文件模板,并输出到 dist 目录下 const appInfo = require('./app/package.json'); // 全部和应用相关的信息从 package.json 读取 const bom = require('gulp-bom'); // 这是为了解析中文的 const outputName = `${appInfo.name}-${platform}-${appInfo.version}-${process.env.NODE_ENV}`; const outputIssName = `${appInfo.name}-${platform}-${process.env.NODE_ENV}.iss` gulp .src(`./build_resource/installer_win_config_${platform}.iss`) .pipe(bom()) .pipe(replace('${version}', appInfo.version)) .pipe(replace('${appExe}', `${appInfo.name}.exe`)) .pipe(replace('${sourcePath}', `${appInfo.name}-${platform}`)) .pipe(replace('${outputName}',outputName)) .pipe(rename(outputIssName)) .pipe(gulp.dest('./dist')) .on('end', () => { // .iss file is ready }) // 2. 交给 inno setup const inno = require('my-gulp-inno'); // 修改后的 gulp-inno gulp .src(`./dist/${outputIssName}`) .pipe(inno()) .on('end', () => { // you have an installer now });
当时还有一个看中 inno setup 的理由是,它可让咱们定制咱们的安装向导步骤和外观,也就是说你可让你的应用也像其余一些优秀的产品同样,在安装的时候能够定制酷炫的外观,能够优化安装流程,支持一键安装,inno setup 仍是能够玩出一些花样的,enjoy。
一样的,安装包也须要代码签名,利用以前封装的签名方法进行签名就好了。
所用工具:appdmg
相比于 windows 的安装包构建,Mac 下的构建安装包又是美滋滋啊,你看我下面小标题都没有就知道了。
// 由于 appdmg 在 windows 下不能下载安装的,因此放在外部 package.json 的 optionalDependencies 下 // 在 gulp 脚本中须要作 try...catch 处理,不然当你回到 windows 下使用这份 gulp 时会出报错 let appdmg; try { appdmg = require('appdmg'); } catch (err) { appdmg = null; } const dmg = appdmg({ // 打出的目标 dmg target: `dist/balabala.dmg`, // 基准目录,如下的资源都基于这个目录 basepath: __dirname, // 具体的选项 specification: { // dmg 打开后的窗口名字 // 注意不要给中文,给中文会致使下面的 background 无效,不明白, github 上也有人提了这个 issue title: `myapp`, // dmg 挂载后的图标,出如今桌面上 icon: "xxx.icns", // 背景图,若是同时存在 bg.png 和 bg@2x.png,appdmg 会根据用户屏幕本身找合适的图 background: "bg.png", // 里面全部icon的尺寸 'icon-size': 96, // 窗口设置 window: { size: { width: 550, height: 320 } }, // 里面的内容,x 是指这个 icon 中心距离窗口最左边的距离,y 是指这个 icon 中心距离窗口顶部的距离 // 这里能够指定一个name项,不要给中文,会致使图标异常 contents: [ { "x": 400, "y": 128, "type": "link", "path": "/Applications" }, { "x": 150, "y": 128, "type": "file", "path": "你的应用.app" } ], // 对 dmg 进行代码签名 'code-sign': { 'signing-identity': '你的代码签名证书' } } }); dmg.on('finish', function () { // you have a dmg now }); dmg.on('error', function (err) { // error });
其他的配置和因此配置影响的内容能够参加 appdmg githug 主页,而后就是本身试试看了。
目的:使应用能够被下载(上一步只是能被安装,但并不能被下载)
作的事:重命名应用安装包供发行,上传应用安装包到云存储服务器供下载
这一步根据每一个人使用的云存储方式不一样而须要利用卖方提供的 API 编写合适的脚本去上传你的安装包,所以具体的脚本不作展开,只是有几点最佳实践能够参考:
当你上传了你的安装包后,也就意味着这个安装包有了一个下载连接,你能够分发这个连接供用户下载啦,至此终于走完了「代码」到可下载「安装包」的过程,鼓掌。
这一路走来看上去已经颇有成就感,但实际上还有许多事能够作得更好,不过工程化的东西,逻辑清晰、流程自动化、能知足需求就能够了,而搭好工程,咱们须要开始专一于 Electron 应用的功能开发了,才刚刚要迈上红地毯,路还有很长,下期见。
对以前的工做流作一个小结(若是遇到有一些旧文件覆盖不了,能够本身加一个清理环节或方法,去清理旧文件)
/* gulpfile.js START */ // 此处省略一堆须要引入的依赖 // 工做参数 let platform = 'win32'; let arch = 'ia32'; let needSign = false; // 配置环节 gulp.task('env', (cb) => { // ... }); // 开发调试 gulp.task('dev', ['env'], (cb) => { exec('electron app --debug', (err) => { if (err) return cb(err); cb(); }); }); // 打包环节 gulp.task('pack',['env'], (cb) => { if (platform === 'darwin') { // ... } else { // ... } }); // 签名环节 gulp.task('sign-pack', ['pack'], (cb) => { if (needSign) { if (platform === 'win32' || platform === 'win64') { // ... } else if (platform === 'darwin') { // ... } } else { cb(); } }); // 构建环节 gulp.task('build', ['sign-pack'], (cb) => { if (platform === 'darwin') { // ... } else { // ... } }); // 发行环节 gulp.task('release', ['build'], (cb) => { // ... }); const codeSignForWin = (filePath) => {...}; const codeSignForMac = (filePath) => {...}; const detectPlatform = () => {...}; /* gulpfile.js END */ // package.json 中配脚本 "scripts": { "yarnall": "yarn && (cd app && yarn)", "start": "cross-env NODE_ENV=dev gulp dev", "packDev": "cross-env NODE_ENV=dev gulp pack", "packAlpha": "cross-env NODE_ENV=alpha gulp pack", "packProd": "cross-env NODE_ENV=prod gulp pack", "buildDev": "cross-env NODE_ENV=dev gulp build", "buildAlpha": "cross-env NODE_ENV=alpha gulp build", "buildProd": "cross-env NODE_ENV=prod gulp build", "releaseDev": "cross-env NODE_ENV=dev gulp release", "releaseAlpha": "cross-env NODE_ENV=alpha gulp release", "releaseProd": "cross-env NODE_ENV=prod gulp release" } // 可选命令行参数: // sign: 是否签名 // platform: 系统平台 // arch: 系统位数