主题切换
第 8 章:应用打包与跨平台发布
开发完成后,你需要将应用打包成可执行文件,分发给用户。本章将学习使用 electron-builder 进行应用打包, 配置 Windows 和 macOS 的打包选项,以及代码签名和发布流程。
8.1 electron-builder 简介
electron-builder 是目前最流行的 Electron 应用打包工具,它可以将你的应用打包成 Windows 的 .exe/.msi、 macOS 的 .dmg/.pkg、Linux 的 .AppImage/.deb 等格式。
💡给前端开发者的建议
把 electron-builder 想象成 Webpack/Vite 的打包阶段,只不过它打包的不是网页,而是完整的桌面应用。 配置方式也很相似——通过配置文件定义入口、输出、插件等。
安装 electron-builder
bash
# 作为开发依赖安装
npm install electron-builder --save-dev
# 或者使用 yarn
yarn add electron-builder --dev8.2 配置 electron-builder
electron-builder 支持多种配置方式:package.json 中的 build 字段、独立的 electron-builder.yml 文件、 或通过命令行参数。推荐使用独立的 YAML 配置文件,更清晰易维护。
基础配置(electron-builder.yml)
yaml
# 应用基本信息
appId: com.yourcompany.yourapp
productName: "RPA Desktop Assistant"
copyright: "Copyright © 2024 Your Company"
# 打包输出目录
directories:
output: dist
buildResources: build
# 文件包含规则
files:
- "out/**/*"
- "node_modules/**/*"
- "package.json"
- "!node_modules/.cache"
- "!**/*.map"
# 额外资源(不打包到 asar 中)
extraResources:
- from: "assets/"
to: "assets"
- from: "templates/"
to: "templates"
# ASAR 配置
asar: true
asarUnpack:
- "node_modules/sharp/**/*"
- "node_modules/@nut-tree/**/*"
# Windows 配置
win:
target:
- target: nsis
arch:
- x64
- ia32
- target: portable
arch:
- x64
icon: build/icon.ico
publisherName: "Your Company"
verifyUpdateCodeSignature: false
# macOS 配置
mac:
target:
- target: dmg
arch:
- x64
- arm64
- target: zip
arch:
- x64
- arm64
icon: build/icon.icns
category: public.app-category.productivity
hardenedRuntime: true
gatekeeperAssess: false
# Linux 配置
linux:
target:
- AppImage
- deb
icon: build/icons
category: Office
# NSIS 安装程序配置(Windows)
nsis:
oneClick: false
allowToChangeInstallationDirectory: true
createDesktopShortcut: always
createStartMenuShortcut: true
shortcutName: "RPA Assistant"
uninstallDisplayName: "RPA Desktop Assistant"
license: "LICENSE.txt"
artifactName: "${productName}-Setup-${version}.${ext}"
# DMG 配置(macOS)
dmg:
sign: false
contents:
- x: 130
y: 220
- x: 410
y: 220
type: link
path: "/Applications"package.json 脚本配置
json
{
"name": "rpa-desktop-assistant",
"version": "1.0.0",
"main": "out/main/index.js",
"scripts": {
"dev": "electron-vite dev",
"build": "electron-vite build",
"preview": "electron-vite preview",
"pack": "electron-builder --dir",
"dist": "electron-builder",
"dist:win": "electron-builder --win",
"dist:mac": "electron-builder --mac",
"dist:linux": "electron-builder --linux",
"postinstall": "electron-builder install-app-deps"
},
"build": {
"extends": "./electron-builder.yml"
}
}8.3 Windows 打包详解
Windows 是最常用的桌面平台,electron-builder 支持多种 Windows 安装包格式。
| 格式 | 扩展名 | 特点 | 适用场景 |
|---|---|---|---|
| NSIS | .exe | 标准安装程序,支持自定义向导 | 大多数场景 |
| Portable | .exe | 免安装,直接运行 | U盘携带、快速试用 |
| MSI | .msi | 企业部署、组策略安装 | 企业环境 |
| 7z/zip | .7z/.zip | 压缩包,无安装向导 | 高级用户 |
Windows 图标配置
bash
# 准备图标文件
# Windows 图标:256x256 像素的 .ico 文件
# 可以使用在线工具将 PNG 转换为 ICO
# 推荐工具:https://convertio.co/zh/png-ico/
# 图标文件路径(在 electron-builder.yml 中配置)
# win.icon: build/icon.ico
# 多尺寸图标(推荐包含 16x16, 32x32, 48x48, 256x256)
# 可以使用 icotools 或 Resource Hacker 创建Windows 代码签名
未签名的 Windows 应用会显示"Windows 已保护你的电脑"警告,影响用户体验。 代码签名需要购买证书(约 ¥1000-3000/年)。
yaml
# electron-builder.yml - 代码签名配置
win:
certificateFile: "build/certificate.p12"
certificatePassword: "your-password"
# 或者使用环境变量
# certificatePassword: ${env.CERT_PASSWORD}
# 环境变量方式(更安全)
# 在 CI/CD 中设置环境变量
# WIN_CSC_LINK: base64 编码的证书
# WIN_CSC_KEY_PASSWORD: 证书密码💡免费代码签名方案
- Certum 开源开发者证书:约 €69/年,验证身份较简单
- Let's Encrypt 不适用:不支持代码签名
- 测试阶段:可以先不签名,正式发布前再购买证书
8.4 macOS 打包详解
macOS 打包相对复杂,需要处理代码签名、公证(Notarization)等流程。
macOS 图标配置
bash
# macOS 图标需要 .icns 格式
# 1. 准备 1024x1024 的 PNG 图标
# 2. 使用 iconutil 或在线工具转换为 .icns
# 使用 iconutil(macOS 自带)
mkdir MyIcon.iconset
sips -z 16 16 icon.png --out MyIcon.iconset/icon_16x16.png
sips -z 32 32 icon.png --out MyIcon.iconset/icon_16x16@2x.png
sips -z 32 32 icon.png --out MyIcon.iconset/icon_32x32.png
sips -z 64 64 icon.png --out MyIcon.iconset/icon_32x32@2x.png
sips -z 128 128 icon.png --out MyIcon.iconset/icon_128x128.png
sips -z 256 256 icon.png --out MyIcon.iconset/icon_128x128@2x.png
sips -z 256 256 icon.png --out MyIcon.iconset/icon_256x256.png
sips -z 512 512 icon.png --out MyIcon.iconset/icon_256x256@2x.png
sips -z 512 512 icon.png --out MyIcon.iconset/icon_512x512.png
sips -z 1024 1024 icon.png --out MyIcon.iconset/icon_512x512@2x.png
iconutil -c icns MyIcon.iconset -o build/icon.icns
rm -rf MyIcon.iconsetmacOS 代码签名与公证
macOS 10.15+ 要求所有分发的应用必须经过公证(Notarization),否则无法运行。 这需要 Apple Developer 账号(¥688/年)。
yaml
# electron-builder.yml - macOS 签名配置
mac:
icon: build/icon.icns
category: public.app-category.productivity
# 启用强化运行时(Notarization 必需)
hardenedRuntime: true
gatekeeperAssess: false
# 权限声明
entitlements: build/entitlements.mac.plist
entitlementsInherit: build/entitlements.mac.plist
# 公证配置(使用 notarize-cli 或 electron-notarize)
# 需要在环境变量中设置 Apple ID 和 App 专用密码
# APPLE_ID: your-apple-id@example.com
# APPLE_APP_SPECIFIC_PASSWORD: xxxx-xxxx-xxxx-xxxx
# APPLE_TEAM_ID: XXXXXXXXXXxml
<?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-dyld-environment-variables</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.get-task-allow</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.files.downloads.read-write</key>
<true/>
</dict>
</plist>⚠️Apple Silicon (M1/M2/M3) 注意事项
如果你使用 Apple Silicon Mac 开发,打包的 x64 版本需要在 Intel Mac 上测试。 建议同时构建 x64 和 arm64 两个版本,或使用 universal 二进制(体积较大)。
8.5 资源文件管理
应用中的静态资源(图标、字体、模板文件等)需要正确配置才能在打包后正常访问。
资源文件路径处理
javascript
const path = require('path');
const { app } = require('electron');
// 判断是否为打包后的应用
const isPackaged = app.isPackaged;
// 获取资源路径的辅助函数
function getResourcePath(relativePath) {
if (isPackaged) {
// 打包后:资源在 app.asar 或 app.getAppPath() 中
return path.join(process.resourcesPath, relativePath);
} else {
// 开发环境:资源在项目根目录
return path.join(__dirname, '..', relativePath);
}
}
// 使用示例
const iconPath = getResourcePath('assets/icon.png');
const templatePath = getResourcePath('templates/report.html');
// 在 Vue 组件中访问静态资源
// 开发环境:直接引用 public 目录下的文件
// 生产环境:使用 app:// 协议或 file:// 协议extraResources 配置
某些文件(如可执行程序、动态库)不能打包到 asar 中,需要放在 extraResources 中:
yaml
extraResources:
# 整个目录
- from: "assets/"
to: "assets"
filter:
- "**/*"
# 单个文件
- from: "bin/ffmpeg"
to: "bin/ffmpeg"
# 根据平台选择不同文件
- from: "bin/win/"
to: "bin"
filter:
- "**/*"
# 排除某些文件
- from: "templates/"
to: "templates"
filter:
- "**/*.html"
- "**/*.css"
- "!**/*.tmp"8.6 打包命令与 CI/CD
配置好打包脚本后,可以通过命令行或 CI/CD 流水线自动构建和发布应用。
常用打包命令
bash
# 打包当前平台
npm run dist
# 仅打包 Windows
npm run dist:win
# 仅打包 macOS(需要在 Mac 上执行)
npm run dist:mac
# 仅打包 Linux
npm run dist:linux
# 生成不打包的安装目录(用于调试)
npm run pack
# 指定架构打包
npx electron-builder --win --x64
npx electron-builder --mac --arm64
npx electron-builder --win --ia32GitHub Actions 自动打包
yaml
# .github/workflows/build.yml
name: Build and Release
on:
push:
tags:
- 'v*'
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 18
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build application
run: npm run build
- name: Package Electron app
run: npx electron-builder --publish always
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# macOS 签名(如果需要)
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
# Windows 签名(如果需要)
WIN_CSC_LINK: ${{ secrets.WIN_CERTIFICATE }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WIN_CERT_PASSWORD }}8.7 发布到 GitHub Releases
electron-builder 可以自动将打包结果上传到 GitHub Releases,配合自动更新功能实现应用分发。
yaml
# electron-builder.yml - 发布配置
publish:
provider: github
owner: your-username
repo: your-repo-name
releaseType: release # 或 draft(草稿)
# 私有仓库需要 token
# token: ${env.GH_TOKEN}
# 或者在 package.json 中配置
"build": {
"publish": {
"provider": "github",
"releaseType": "release"
}
}💡打包最佳实践
- 版本号管理:使用语义化版本(semver),如 1.2.3
- 构建前清理:确保每次构建都是干净的,删除旧的构建产物
- 测试安装包:在干净的虚拟机中测试安装和卸载流程
- 文件大小优化:排除不必要的依赖和文件,使用 asar 压缩
- 多架构支持:同时提供 x64 和 arm64 版本(macOS)
8.8 国际化(i18n)
Electron 应用国际化涉及两个层面:界面文本和原生系统文本(菜单、对话框)。良好的国际化支持能让你的应用触达全球用户。
Vue3 项目中的界面国际化
推荐使用 vue-i18n@9:
bash
npm install vue-i18n@9javascript
// renderer/src/i18n/index.js
import { createI18n } from 'vue-i18n'
import zh from './locales/zh.json'
import en from './locales/en.json'
const i18n = createI18n({
locale: 'zh', // 默认语言
fallbackLocale: 'en', // 回退语言
messages: { zh, en }
})
export default i18nvue
<template>
<div>
<h1>{{ $t('welcome') }}</h1>
<button @click="switchLang">{{ $t('switchLanguage') }}</button>
</div>
</template>
<script setup>
import { useI18n } from 'vue-i18n'
const { locale } = useI18n()
function switchLang() {
locale.value = locale.value === 'zh' ? 'en' : 'zh'
// 持久化用户选择
localStorage.setItem('app-locale', locale.value)
}
</script>原生菜单国际化
根据系统语言动态构建菜单:
javascript
// main.js
const { app, Menu } = require('electron')
const zhMenu = [
{ label: '文件', submenu: [{ label: '打开', role: 'open' }] },
{ label: '编辑', submenu: [{ label: '撤销', role: 'undo' }] }
]
const enMenu = [
{ label: 'File', submenu: [{ label: 'Open', role: 'open' }] },
{ label: 'Edit', submenu: [{ label: 'Undo', role: 'undo' }] }
]
function createMenu() {
const locale = app.getLocale()
const template = locale.startsWith('zh') ? zhMenu : enMenu
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
}
app.whenReady().then(() => {
createMenu()
})安装包多语言配置
在 electron-builder.yml 中配置 Windows 安装程序的多语言支持:
yaml
nsis:
oneClick: false
allowToChangeInstallationDirectory: true
installerLanguages: ['zh_CN', 'en_US', 'ja_JP']
language: '2052' # 默认中文
# macOS 使用系统原生语言,无需额外配置动态切换语言
javascript
// ipc.js - 处理语言切换
ipcMain.handle('app:set-locale', (event, locale) => {
// 更新菜单
createMenu()
// 通知所有窗口更新语言
BrowserWindow.getAllWindows().forEach(win => {
win.webContents.send('locale-changed', locale)
})
})国际化最佳实践
- 提取时机:项目初期就提取文本,避免后期大规模替换
- 键名规范:使用结构化键名(如
settings.appearance.theme),便于维护 - 占位符:支持动态内容的插值(如
Hello, {name}!) - RTL 语言:阿拉伯语、希伯来语等需要 RTL 布局支持,使用 CSS
direction: rtl - 日期/数字:使用
Intl.DateTimeFormat和Intl.NumberFormat格式化本地内容
常见问题
- 系统语言检测:
app.getLocale()返回如zh-CN、en-US,注意大小写和连字符 - 菜单热键:不同语言的菜单项需要重新计算快捷键(如
Ctrl+O的O对应中文"打开"没有明显字母关联) - 安装包大小:每增加一种语言,安装包会增大(包含翻译文件和安装程序语言包)