使用 electron-builder 与 electron-packager 构建 electron 桌面应用安装程序

目录
[隐藏]

electron-builderelectron-packager 都可以用来将 webapp 通过 Electron 应用打包为桌面客户端然后发布。

这两个工具都支持命令行工具和 JSAPI 方式。在有复杂的多环境构建需求的情况下,通过 JSAPI 定制不同的参数更为方便可控。但是由于参数众多的原因,实现想要的效果需要注意较多的细节,可能会采一些坑才能达到目的。

本文将分别简要介绍使用 electron-builderelectron-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
点赞 (2)

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

Captcha Code