6月4日 14:01

Electron打包分发:签名、公证、自动更新和体积优化

Electron 应用写完了不算完——打包、签名、分发、自动更新,每一步都有坑。Windows 上没签名的安装包会被 SmartScreen 拦截,macOS 上没公证的应用直接打不开,安装包体积动辄 150MB+ 用户嫌大。这篇文章把打包到分发的完整流程走一遍。

打包工具选择

工具特点适合谁
electron-builder功能最全,签名+更新+多格式一步到位生产环境首选
electron-forge官方推荐,集成开发+打包+发布流程新项目开箱即用
electron-packager只打包不安装包,功能简单只需要可执行文件

大部分项目选 electron-builder 就对了。

electron-builder 配置

bash
npm install --save-dev electron-builder

在 package.json 里配置:

json
{ "build": { "appId": "com.yourcompany.yourapp", "productName": "YourApp", "directories": { "output": "dist" }, "files": [ "build/**/*", "node_modules/**/*", "package.json" ], "win": { "target": [{ "target": "nsis", "arch": ["x64"] }], "icon": "build/icon.ico" }, "mac": { "target": [{ "target": "dmg", "arch": ["x64", "arm64"] }], "icon": "build/icon.icns", "category": "public.app-category.productivity", "hardenedRuntime": true, "gatekeeperAssess": false, "entitlements": "build/entitlements.mac.plist", "entitlementsInherit": "build/entitlements.mac.plist" }, "linux": { "target": ["AppImage", "deb"], "icon": "build/icon.png", "category": "Utility" } } }

打包命令:

bash
npx electron-builder --win # Windows npx electron-builder --mac # macOS npx electron-builder --linux # Linux npx electron-builder -mwl # 全平台(需要在对应系统上跑)

NSIS 安装程序(Windows)

NSIS 是 Windows 上最常用的安装包格式:

json
{ "build": { "nsis": { "oneClick": false, "allowToChangeInstallationDirectory": true, "createDesktopShortcut": true, "createStartMenuShortcut": true, "shortcutName": "YourApp", "uninstallDisplayName": "YourApp", "license": "LICENSE.txt" } } }

oneClick: false 让用户选择安装目录,而不是一闪而过安装完。createDesktopShortcut 看似方便,但很多用户反感桌面图标——建议设为 alwaysCreate: false 让用户自己勾选。

代码签名

不签名的应用会被操作系统拦截:Windows 的 SmartScreen 弹蓝框,macOS 的 Gatekeeper 直接说"无法验证开发者"。

Windows 签名

需要购买代码签名证书(EV 或 Standard)。EV 证书签名后 SmartScreen 立即信任,Standard 证书需要积累信誉。

bash
# 环境变量方式(CI/CD 推荐) export CSC_LINK=path/to/certificate.pfx export CSC_KEY_PASSWORD=your-password npx electron-builder --win

electron-builder 检测到 CSC_LINK 环境变量后自动签名,不用额外配置。

macOS 签名和公证

macOS 要求应用同时签名和公证(Notarization),否则用户打开时会弹"无法验证开发者"。

签名需要 Apple Developer 证书,公证需要 Apple ID:

bash
export CSC_LINK=path/to/developer-id.p12 export CSC_KEY_PASSWORD=your-password export APPLE_ID=your@email.com export APPLE_APP_SPECIFIC_PASSWORD=xxxx-xxxx-xxxx-xxxx export APPLE_TEAM_ID=XXXXXXXXXX npx electron-builder --mac

electron-builder 在 mac.hardenedRuntime: true 的情况下会自动签名并提交公证。公证过程需要 1-5 分钟,期间应用无法分发。

entitlements.mac.plist 文件(声明权限):

xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>com.apple.security.cs.allow-jit</key> <true/> <key>com.apple.security.cs.allow-unsigned-executable-memory</key> <true/> <key>com.apple.security.cs.allow-dyld-environment-variables</key> <true/> </dict> </plist>

Electron 需要这三个权限:JIT(V8 引擎)、unsigned memory(渲染进程)、dyld variables(native 模块加载)。不声明的话签名后应用会崩溃。

自动更新

electron-updater 是 electron-builder 配套的自动更新方案,支持差分更新(只下载变化部分):

bash
npm install electron-updater
javascript
const { autoUpdater } = require('electron-updater') const log = require('electron-log') autoUpdater.logger = log autoUpdater.autoDownload = false // 不自动下载,先提示用户 app.whenReady().then(() => { autoUpdater.checkForUpdates() autoUpdater.on('update-available', (info) => { // 通知用户有新版本 dialog.showMessageBox({ type: 'info', title: '发现新版本', message: `新版本 ${info.version} 可用,是否现在下载?`, buttons: ['下载', '稍后'] }).then(({ response }) => { if (response === 0) autoUpdater.downloadUpdate() }) }) autoUpdater.on('update-downloaded', () => { dialog.showMessageBox({ type: 'info', title: '更新就绪', message: '新版本已下载,重启应用以完成安装。', buttons: ['立即重启', '稍后'] }).then(({ response }) => { if (response === 0) autoUpdater.quitAndInstall() }) }) })

更新源配置在 package.json:

json
{ "build": { "publish": { "provider": "github", "owner": "your-username", "repo": "your-repo" } } }

支持 GitHub Releases、S3、通用 HTTP 服务器。发布新版本时,electron-builder 自动把安装包上传到 GitHub Releases,autoUpdater 检查 latest.yml 判断是否有更新。

体积优化

Electron 应用默认 150MB+,因为包含了完整的 Chromium。可以压缩:

排除不需要的文件

json
{ "build": { "files": [ "build/**/*", "!build/samples/**/*", "node_modules/**/*", "!node_modules/*/test/**/*", "!node_modules/*/docs/**/*", "!node_modules/*.md" ] } }

! 开头表示排除。test、docs、README 等文件打包后不需要。

asar 归档

asar 把源码打包成只读归档,减少文件数量和体积:

json
{ "build": { "asar": true, "asarUnpack": [ "node_modules/native-module/**/*" // native 模块不能放进 asar ] } }

native 模块(better-sqlite3、keytar 等)必须 unpack,因为它们需要加载 .node 动态库,asar 里的文件不能直接 dlopen。

双架构 vs 通用二进制

macOS 支持 Universal 二进制(同时包含 x64 和 arm64),但体积翻倍。如果不需要 Rosetta 兼容,单独打 arm64 体积小一半:

json
{ "build": { "mac": { "target": [{ "target": "dmg", "arch": ["arm64"] }] } } }

M1/M2/M3 用户占 macOS 大多数,只打 arm64 够用。需要兼容 Intel 的场景再打 Universal。

CI/CD 自动化

用 GitHub Actions 自动打包:

yaml
name: Build on: push jobs: build: strategy: matrix: os: [windows-latest, macos-latest, ubuntu-latest] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: { node-version: 20 } - run: npm ci - run: npx electron-builder --publish always env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} CSC_LINK: ${{ secrets.CSC_LINK }} CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}

关键点:每个平台必须在对应 OS 上打包。Windows 安装包不能在 macOS 上交叉编译(签名工具不兼容)。macOS 公证也必须在 macOS 上跑。

标签:Electron