electron-builder
与 electron-packager
都可以用来将 webapp 通过 Electron 应用打包为桌面客户端然后发布。
这两个工具都支持命令行工具和 JSAPI 方式。在有复杂的多环境构建需求的情况下,通过 JSAPI 定制不同的参数更为方便可控。但是由于参数众多的原因,实现想要的效果需要注意较多的细节,可能会采一些坑才能达到目的。
本文将分别简要介绍使用 electron-builder
与 electron-packager
的 JSAPI 实现打包应用程序的一些实践细节。
1 项目主要目录结构
我们设定项目构建相关的目录结构如下:
├─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
打包构建的执行需要使用系统管理员权限启动。核心代码示例:
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 * https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#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
,示例如下:
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); });
命令行打包的方式也比较简单:
.\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 系统平台下进行。
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 系统平台下进行。
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
可以直接构建输出用于安装的程序包,配置项上也因此更为灵活多样。核心示例代码:
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 * https://electron.build/configuration/configuration */ 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
文件并配置相关配置项,内容参考如下:
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