electron-builder
与 electron-packager
都可以用来将 webapp 通过 Electron 应用打包为桌面客户端然后发布。
这两个工具都支持命令行工具和 JSAPI 方式。在有复杂的多环境构建需求的情况下,通过 JSAPI 定制不同的参数更为方便可控。但是由于参数众多的原因,实现想要的效果需要注意较多的细节,可能会采一些坑才能达到目的。
本文将分别简要介绍使用 electron-builder
与 electron-packager
的 JSAPI 实现打包应用程序的一些实践细节。
1 项目主要目录结构
我们设定项目构建相关的目录结构如下:
1 2 3 4 5 6 7 8 9 | ├─src web 应用源码目录 ├─electron Native 应用构建相关的源码目录 │ ├─resources 各种资源文件 │ └─src Native 逻辑源码目录 ├─dist │ ├─electron 构建APP所有需要的资源都需先输出至该目录 │ │ ├─app dist/webapp 的副本,用于打包进 Native App 中的本地应用 │ │ └─resources │ └─webapp web 应用构建输出后的资源目录 |
示例 DEMO 项目参考:github-user-search-vue
2 使用 electron-packager 打包 Electron 应用
electron-packager
可以简单方便地打包出各种系统环境下未压缩的免安装版本。然后再使用其他工具将这些免安装目录打包成安装程序,即可达到目的。
electron-packager
打包构建的执行需要使用系统管理员权限启动。核心代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | const chalk = require( 'chalk' ); const path = require( 'path' ); const packager = require( 'electron-packager' ); // const buildSrc = require('./build-src'); // const CFG = require('./src/electron.config'); const argv = require( 'minimist' )(process.argv.slice(2)); const startPack = () => { const packEnv = 'test' ; // CFG.getPackEnv(argv.env); const resDir = path.join(__dirname, `./resources/res-${packEnv}`); const platform = argv.platform || process.env.PACK_PLATFORM || 'all' ; /** * `electron-packager` options */ const opts = { // Target 'x64' architecture arch: process.env.PACK_ARCH || 'x64' , // 'ia32' // Compress app using 'electron/asar' asar: true , // The source directory dir: path.join(__dirname, '../dist/electron' ), // Set electron app icon File extensions are added based on platform icon: path.join(resDir, `app.ico`), // Ignore files that would bloat final build size ignore: /(^\/(src|test|\.[a-z]+|README|yarn| static |dist\/web))|\.gitkeep/, // Save builds to `builds` out: path.join(__dirname, `../release/packager-gus-${packEnv}`), // Overwrite existing builds overwrite: true , electronVersion: '3.0.10' , // appVersion: pkg.version, appCopyright: `Copyright(C) ${ new Date().getFullYear()} lzwme, Inc. All rights reseved.`, platform, // Windows targets only win32metadata: { CompanyName: 'lzwme' , }, /** OS X/Mac App Store targets only */ appBundleId: `com.lzwme.gus-${packEnv}`, osxSign: false , }; console.log(chalk.yellow.bold( 'Release For Env: ' ), chalk.bold.green(packEnv)); console.log(chalk.cyan.bold( 'Release To:' ), opts.out, '\n' ); return packager(opts).then(appPaths => { console.log(chalk.green.bold( '\n Done!' )); }). catch (err=> { console.log(err); }); } startPack(); // buildSrc.start().then(() => startPack()); |
在 windows 下的构建结果示例:
2.1 使用 electron-installer-windows 打包为 windows 下的安装程序
electron-packager
构建出来的结果是免安装的目录。我们可以继续使用其他工具将他们分别打包为可安装的应用。
对于 windows 系统,可以采用 electron-installer-windows
,示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | const installer = require( 'electron-installer-windows' ); const options = { src: 'release/packager-gus-test/lzwme-gus-test-win32-x64/' , dest: 'release/packager-gus-test/' , icon: 'resources/icon.ico' , }; console.log( 'Creating package (this may take a while)' ); installer(options) .then(() => console.log(`Successfully created package at ${options.dest}`)) . catch (err => { console.error(err, err.stack); process.exit(1); }); |
命令行打包的方式也比较简单:
1 | .\node_modules\.bin\electron-installer-windows --src release /packager-gus-test/lzwme-gus-test-win32-x64/ --dest release /packager-gus-test/ --icon dist /electron/resources/app .ico |
不过这里的执行高概率会报错,重新执行可能会成功,具体原因暂时未知。
2.2 使用 electron-osx-sign 给 macOS 的应用签名
该操作应当在 macOS 系统平台下进行。
1 2 3 4 5 6 7 8 9 10 | const signAsync = require( 'electron-osx-sign' ).signAsync; signAsync({ app: 'release/packager-gus-test/lzwme-gus-test-mas-x64/lzwme-gus-test.app' , }) .then( function () { console.log( 'Application signed' ); }) . catch ( function (err) { console.log(err); }); |
2.3 使用 electron-installer-dmg 打包 mac 下的 dmg 安装包
该操作只能在 macOS 系统平台下进行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | const createDMG = require( 'electron-installer-dmg' ); createDMG({ appPath: 'release/packager-gus-test/lzwme-gus-test-mas-x64/lzwme-gus-test.app' , name: 'gus-test' , // The title of the produced DMG, which will be shown when mounted. title: 'lzwme-gus-test' , overwrite: true , out: 'release/packager-gus-test' , icon: 'resources/app.png' , iconSize: '' , }, function done (err) { if (err) { console.log(err); return ; } console.log( 'done!' ); }); |
3 使用 electron-builder 打包 Electron 应用
electron-packager
构建出来的结果是免安装的目录,效果很好,速度很快。但还需借助其它的插件打包为可安装程序包。
相比较而言,electron-builder
可以直接构建输出用于安装的程序包,配置项上也因此更为灵活多样。核心示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | const chalk = require( 'chalk' ); const builder = require( 'electron-builder' ); const Platform = builder.Platform; const path = require( 'path' ); const CFG = require( './src/electron.config' ); const buildSrc = require( './build-src' ); const argv = require( 'minimist' )(process.argv.slice(2)); const packEnv = CFG.getPackEnv(argv.env); const platform = argv[ 'platform' ] || 'all' ; const resDir = path.join(__dirname, `./resources/res-${packEnv}`); /** * `electron-builder` options */ const opts = { appId: `com.lzwme.gus-${packEnv}.app`, icon: path.join(resDir, `app_256x256.png`), productName: `gus-${packEnv}`, artifactName: '${productName}-${platform}-${arch}_${version}.${ext}' , electronVersion: '3.0.10' , // remoteBuild: false, directories: { app: path.join(__dirname, '../dist/electron' ), buildResources: path.join(__dirname, `./resources/res-${packEnv}`), output: path.join(__dirname, `../release/builder-gus-${packEnv}`), }, dmg: { icon: path.join(resDir, `app.icns`), }, linux: { target: [ 'zip' ], icon: path.join(resDir, `app_256x256.png`), }, mac: { target: [ 'dmg' , 'zip' , 'pkg' ], icon: path.join(resDir, `app.icns`), }, win: { target: [ 'nsis' , '7z' ], // , , 'zip', 'msi' icon: path.join(resDir, `app_256x256.ico`), }, nsis: { oneClick: false , perMachine: true , allowToChangeInstallationDirectory: false , installerHeaderIcon: path.join(resDir, `app_256x256.ico`), installerIcon: path.join(resDir, `app_256x256.ico`), // installerHeader: path.join(resDir, `app_256x256.png`), // installerSidebar: path.join(resDir, `app_256x256.png`), // installerHeader: 'installerHeader.bmp', // include: 'windows/installer.nsh', // script: 'windows/installer.nsi', // publish: {} }, // publish: [{ // "provider": "generic", // //类似于autoUpdater.setFeedURL(url)中的url,用于自动更新的文件地址 // "url": "http://www.xxx.com/" // }], afterPack(packer) { console.log( 'packed!' ); }, }; const buildStart = async () => { console.log(chalk.yellow.bold( 'Release For Env: ' ), chalk.bold.green(packEnv)); // 开启调试模式 if (argv.debug) { process.env.DEBUG = 'electron-builder' ; } if ([ 'win' , 'win32' , 'all' ].includes(platform)) { await builder.build({ targets: Platform.WINDOWS.createTarget(), config: opts, }); } if ([ 'linux' , 'all' ].includes(platform)) { await builder.build({ targets: Platform.LINUX.createTarget(), config: opts, }); } // 只有在 mac 平台上才构建 mac 版本 if (process.platform === 'darwin' && [ 'mac' , 'all' ].includes(platform)) { await builder.build({ targets: Platform.MAC.createTarget(), config: opts, }); } } buildStart(); |
在 windows 下的构建结果示例:
4 常见问题
4.1 electron-builder
安装过程中 electron 等下载失败
主要可能会出现 electron、winCodeSign、nsis、nsis-resources、wix 等资源包因网络缓慢、被 GFW 而导致下载失败。解决方法参考如下:
1 设置 electron
等从淘宝镜像下载。可设置环境变量,或者在项目目录下增加 .npmrc
文件并配置相关配置项,内容参考如下:
1 2 3 4 5 | ELECTRON_MIRROR=http: //npm .taobao.org /mirrors/electron/ ELECTRON_BUILDER_BINARIES_MIRROR=http: //npm .taobao.org /mirrors/electron-builder-binaries/ SELENIUM_CDNURL=http: //npm .taobao.org /mirrorss/selenium CHROMEDRIVER_CDNURL=https: //npm .taobao.org /mirrors/chromedriver SASS_BINARY_SITE=https: //npm .taobao.org /mirrors/node-sass/ |
2 nsis
等的下载报错,可根据错误信息中的提示,手动使用迅雷等下载工具下载相关包,然后解压至临时的缓存目录(一般为 C:\Users\<username>\AppData\Local\electron-builder\Cache
)。然后再次重新安装,则会优先从缓存目录获取内容,不再从网络下载。
以 nsis
为例,参考如下图示例:
参考命令报错提示,手动下载相关文件并解压至 目录。
5 相关参考
- https://github.com/electron-userland/electron-packager
- https://github.com/electron-userland/electron-installer-windows
- https://github.com/electron-userland/electron-installer-dmg
- https://github.com/electron-userland/electron-builder
- https://www.electron.build/api/electron-builder