electron + react 开发实践:electron-builder 配置

背景

公司的一个紧急项目,需要一个windows客户端,接收控制端的指令在大屏幕上播放对应的素材。

因为安卓方案在大屏幕上的播放效果不佳,随决定使用windows方案。


方案

直接使用现成的脚手架 electron-react-boilerplate

主要的开发依赖:

  • typescript
  • electron-builder 打包工具,配置项在 package.json 里的 build 字段
  • webpack 配套
  • less (脚手架默认是sass,因为要下载node-sass挺麻烦的,我就替换成了less)
  • 其余依赖…

主要依赖:
- react 全家桶
- dayjs 时间处理
- crypto-js 需要md5
- electron-log 日志处理
- electron-store 简单的持久化存储(json)
- node-fetch nodejs的fetch api
- node-schedule nodejs定时任务
- adm-zip 处理zip
- better-sqlite3 原生模块sqlite数据库

开始

第一步

下载脚手架模板并安装依赖
1
2
3
git clone --depth 1 --branch main https://github.com/electron-react-boilerplate/electron-react-boilerplate.git your-project-name
cd your-project-name
npm install

第二步(不修改项目配置可以跳过)

先修改 package.json 的配置,根据个人的需要安装一些第三方依赖以及整体项目的配置,这里以现在这个项目为例子分享一下我的修改。
- 修改其余包含 electron-react-boilerplate 的配置项,包括 https://github.com/electron-react-boilerplate/electron-react-boilerplate 这些地址的选项全都更换成自己的
- 主要修改 build 字段配置,下面先简单介绍一下这个字段

build 字段

列出的是我修改的配置字段(各个平台的打包配置项几乎没改都是使用默认的),完整的配置项有很多,可以前往官网查看

通用配置

  • productName 程序的产品名称,如果定义了这个字段,那么安装exe之后的可执行文件就是以这个命名。如果没在 build 中定义,那么会取 package.json 中的 productName,如果也没定义,就会取 name 字段
  • appId 应用id,默认为 com.electron.${name},官方强烈建议自己定义id
  • electronDownload electron下载配置
    • electronDownload.version 指定版本号
    • electronDownload.cache 指定缓存地址
    • electronDownload.mirror 指定镜像,如果不指定镜像,会从github下载,国内用户需要设置,一般都是使用淘宝源: https://npmmirror.com/mirrors/electron/
    • 其他配置
  • artifactName 最终打包后的可执行文件的名称,默认是 ${productName}-${version}.${ext},可以使用[这些](https://www.electron.build/file-patterns#file-macros)重命名,我的话改成了 ${id}-${version}-${env.BUILD_ENV}.${ext},这个 env.BUILD_ENV 是我在 package.json 的 scripts 中添加的一个环境变量
  • executableName 可执行文件的文件名,默认是 ${productName}
  • asar true/false 是否使用 electron 的归档格式,如果为true, app 就会被打包成 app.asar
  • publish 应用发布配置,我直接移除了这个字段。

Hooks

  • afterPack 打包之后(代码签名和生成可执行程序之前)执行的函数。
  • afterAllArtifactBuild 所有构建都完成后执行的函数

我修改的配置

1
2
3
4
5
6
7
8
9
10
11
12
{
"build": {
"productName": "嘻嘻嘻",
"appId": "com.dddreee.electron"
"electronDownload": {
"mirror": "https://npmmirror.com/mirrors/electron/"
},
"artifactName": "${id}-${version}-${env.BUILD_ENV}.${ext}",
},
"beforePack": "./.erb/scripts/beforePack.js",
"afterAllArtifactBuild": "./.erb/scripts/afterAllArtifactBuild.js"
}
beforePack.js
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
/**
* 注意必须要 exports.default
*/
const fs = require('fs')
const path = require('path')
const childProcess = require('child_process')

exports.default = async function(context) {
const isProd = process.env.BUILD_ENV === 'prod' // 我定义的正式环境是prod

// git describe --tags 获取当前tag的具体信息 比如之前打的tag 是 v1.0.0
// 每次提交之后都会在这个tag的基础上加上提交的次数,提交了2次之后执行这个命令返回的就是 v1.0.0-2
const data = childProcess.execSync(`git describe --tags`).toString('utf8')
const tagVersion = data.split('\n')[0].split('-')[1]
const versionInfo = {
appId: context.packager.appInfo.id,
// 打非正式环境的包的时候,可以在版本后边加一个tag的提交数
version: isProd ? `${context.packager.appInfo.version}.000` : `${context.packager.appInfo.version}.${tagVersion}`,
name: context.packager.appInfo.productName,
env: process.env.BUILD_ENV
}
const vesionInfoStr = JSON.stringify(versionInfo)
fs.createWriteStream(path.join(context.outDir, 'version.json')).write(vesionInfoStr)
}

afterAllArtifactBuild.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 注意必须要 exports.default
*/

const path = require('path')
const AdmZip = require('adm-zip')
const { readFileSync } = require('fs')

exports.default = (buildResult) => {
const exeFile = buildResult.artifactPaths.find(path => path.endsWith('.exe'))
const versionJsonPath = path.join(buildResult.outDir, 'version.json')
const versionInfo = JSON.parse(readFileSync(versionJsonPath).toString())
// 将 version.json 和 exe安装包 一起添加到压缩文件
const zip = new AdmZip()
zip.addLocalFile(exeFile)
zip.addLocalFile(versionJsonPath)
const zipPath = path.join(buildResult.outDir, `app.zip`)
zip.writeZip(zipPath)
}

结束

开发时间很仓促,electron-builder 的配置项还有很多没去研究,后续项目中有变动的时候再记录一下吧