electron 开发经验之谈系列-使用 electron-builder 打包

本文最后更新于:2021年2月19日 下午

前言

基本项目搭建 中,我们已经能够启动一个 electron 应用程序了(开发环境),现在来看如何将之打包为二进制程序便于分发给最终用户。

依赖

渲染层打包

直接使用 cra 的打包工具即可,没什么大不了的,但确实存在一些注意事项

  • 因为 electron 在生产环境会从文件系统中加载静态资源,所以打包出来的静态资源必须支持相对路径,下面是常见的两个设置。

主进程打包

electron-builder 打包需要以下几个步骤

更新 package.json 的一些配置

  1. 使用 package.json 中的 build 字段作为配置项,参考: https://www.electron.build/
  2. electron-builder 使用 main 字段作为启动脚本文件
  3. electron-builder 要求必须使用固定的版本号,意味着 electron 依赖需要指定为 "electron": "10.2.0"

下面是一个基本的配置示例

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
{
"main": "dist/main.js",
"scripts": {
// 一个非常基本的打包脚本
"pkg": "electron-builder"
},
"devDependencies": {
"electron": "10.2.0",
"electron-builder": "^22.9.1",
// electron 主进程实际上是 nodejs 环境,所以为了更好的开发体验,安装 nodejs 的类型定义
"@types/node": "^12.19.12"
},
"build": {
// 程序的唯一标识符
"appId": "com.rxliuli.electron_example",
// 打包出来的 exe 名字
"productName": "electron 示例应用",
// 打包的目录
"directories": {
"output": "release"
},
"win": {
// 打包目标,参考: https://www.electron.build/
"target": ["nsis"]
}
}
}

复制静态资源

现在,我们需要打包静态资源并复制到主进程模块里面

  1. cd apps/renderer 目录
  2. yarn build 打包静态资源
  3. 将静态资源复制到 build/dist 目录下

修改主进程入口文件 main.ts

还需要修改 src/main.ts 代码,主要修改 BrowserWindow 对象载入的 url 地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { app, BrowserWindow } from 'electron'
import path = require('path')
import { URL } from 'url'

async function createMainWindow() {
// 创建新的 electron 窗口
const mainWindow = new BrowserWindow()
// 载入生产环境的 url
await mainWindow.loadURL(
new URL(path.join(__dirname, './build/index.html')).href,
)
}

// 其他代码...

注意,这个 url 路径是相对于打包后的 dist/main.js 而言的,因为最终打包的程序运行时的相对路径也是这样。

打包主进程的 exe 程序

因为 electron-builder 需要下载基本的 electron 程序,所以请提前设置好透明代理,如果不知道它是什么,参考: 透明网关Proxifier

  1. 使用 yarn compile 编译 ts 代码
  2. 使用 yarn pkg 打包 electron 应用

现在,我们应该可以在 apps/main/release/win-unpacked 下看到 exe 程序,双击它即可看到之前在开发环境的首页了。

参考: https://github.com/rxliuli/electron_example/tree/85d398fc2c6ba6c918ad9641dbb5d8bae2d4216b/apps/main

优化打包

虽然打包已经实现,但确实还存在一些问题

  • 打包脚本仍然不是一键的
  • 不能兼容开发、生产环境

下面我们来解决这两个问题

实现一键打包二进制程序

使用 gulp 复制渲染层的静态资源

1、添加 gulp 相关依赖 yarn add -D gulp ts-node @types/gulp fs-extra @types/fs-extra

  • gulp @types/gulp: gulp 核心依赖
  • ts-node: 使用 ts 编写 gulp 脚本必须的依赖
  • fs-extra @types/fs-extra: fs 的扩展增强,使用 Promise 包装异步 api

2、添加 gulp 脚本文件

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
import { copy, remove } from 'fs-extra'
import * as path from 'path'

async function copyByMap(copyMap: [string, string][]) {
await Promise.all(
copyMap.map(async ([src, destDir]) => {
const srcPath = path.resolve(__dirname, src)
const destPath = path.resolve(__dirname, destDir, path.basename(srcPath))
await copy(srcPath, destPath)
}),
)
}

/**
* 清理最终生成目录
*/
export async function clean() {
await remove(path.resolve(__dirname, 'dist'))
await remove(path.resolve(__dirname, 'release'))
}

/**
* 复制一些资源到 dist 目录中
*/
export async function copyStatic() {
await copyByMap([['../renderer/build', 'dist/']])
}

3、添加 npm script

注: lerna 的好处之一就是可以运行其它模块的 npm script。

1
2
3
4
5
6
7
8
{
"scripts": {
// 打包渲染层的静态资源
"build:web": "lerna run --scope renderer build",
// 打包渲染层的静态资源之后复制然后使用 electron-builder 打包 exe 程序
"pkg": "gulp clean && yarn compile && yarn build:web && gulp copyStatic && electron-builder"
}
}

4、修改 tsconfig.json

此时在 apps/main 模块根目录下也有 ts 文件了,所以 tsc 翻译代码会将它们也包含进去,但实际上不需要。

1
2
3
{
"include": ["src"]
}

现在,我们可以使用一个命令打包 exe 程序了。

参考: https://github.com/rxliuli/electron_example/blob/3dacff5dc0/apps/main/package.json

使用环境变量来兼容开发、生产环境

解决方案简单来说就一句话:使用环境变量指定开发环境的 URL。
这里使用 env-cmd 来跨平台写入环境变量(不使用 dotenv 的原因在于自定义环境使用起来有点麻烦,不像 env-cmd 那么直观),而另一个 cross-env 并未提供管理环境变量的解决方案。下面说一下使用 env-cmd 的步骤

  1. 安装依赖 yarn add -D env-cmd

  2. 添加配置文件 .env-cmdrc.json
    基本上是一个键值映射文件,键是环境,值对象是环境变量

    1
    2
    3
    4
    5
    {
    "dev": {
    "ELECTRON_START_URL": "http://localhost:3000/"
    }
    }
  3. dev:electron 命令之前设定环境变量 env-cmd -e dev electron ./dist/main.js

  4. 修改 src/main.ts 读取环境变量

    1
    2
    3
    4
    await mainWindow.loadURL(
    process.env.ELECTRON_START_URL ||
    path.join(__dirname, './build/index.html'),
    )

现在,像 基本项目搭建更新 package.json 添加几个 npm script 说的那样启动开发环境就会显示开发环境的页面,打包后显示的则是打包后的静态资源。

效果

参考: https://github.com/rxliuli/electron_example/blob/f8b4f94435/apps/main/.env-cmdrc.json

总结

虽然 electron-builder 已经足够好用了,但它仍然不能解决 electron 项目工程上的问题,所以这里结合了 lerna/gulp/env-cmd 打包。


electron 开发经验之谈系列-使用 electron-builder 打包
https://blog.rxliuli.com/p/33dd9a3fccaf4666b04935237f885772/
作者
rxliuli
发布于
2021年1月7日
许可协议