Electron 应用打包实战:从 Vite + React 项目到 Windows 安装包
Electron 应用打包实战:从 Vite + React 项目到 Windows 安装包
做桌面端应用时,Electron 几乎是绕不开的技术栈:Chromium 内核负责 UI,Node.js 负责系统能力,两者结合让前端技术栈直接覆盖桌面场景。但真正把一个 Electron 项目从"能开发"推到"能交付",中间横着不少坑。本文以 Vite + React + TypeScript 的项目结构为例,完整走一遍从工程初始化到产出 Windows 安装包的全流程,并重点解释几个最容易踩的雷。
一、为什么选 Vite 而不是 webpack
传统 Electron 模板用的是 webpack,配 Electron 打包插件时构建动辄一两分钟起步,热更新更是要等。Vite 的 dev server 走原生 ESM,冷启动基本是秒级,对 Electron 主进程 + 渲染进程的双进程结构也支持得很好。
关键是理解 Vite 在 Electron 里的两套构建目标:渲染进程用标准 web 配置即可,而主进程是 Node 环境,不能用浏览器端的构建目标,否则会踩到 `require is not defined` 这类问题。
// electron.vite.config.ts
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
main: {
plugins: [externalizeDepsPlugin()],
build: {
rollupOptions: {
input: { index: 'src/main/index.ts' }
}
}
},
preload: {
plugins: [externalizeDepsPlugin()],
build: {
rollupOptions: {
input: { index: 'src/preload/index.ts' }
}
}
},
renderer: {
root: 'src/renderer',
resolve: { alias: { '@renderer': 'src/renderer/src' } },
plugins: [react()],
build: {
rollupOptions: {
input: { index: 'src/renderer/index.html' }
}
}
}
})
`externalizeDepsPlugin()` 是这里最关键的一行:它把 `dependencies` 里的包从打包产物中排除,改为运行时从 `node_modules` 加载。这样打包体积能小很多,同时避免了 Electron 内置 Node 版本与依赖版本不兼容导致的诡异报错。
二、主进程、预加载、渲染进程的三层边界
Electron 的安全模型要求渲染进程默认拿不到 Node 能力,所有系统级操作都必须经过预加载脚本桥接。
// src/preload/index.ts
import { contextBridge, ipcRenderer } from 'electron'
const api = {
getVersion: () => ipcRenderer.invoke('app:getVersion'),
openFile: (filePath: string) => ipcRenderer.invoke('dialog:openFile', filePath)
}
if (process.contextIsolated) {
contextBridge.exposeInMainWorld('electronAPI', api)
} else {
// @ts-ignore
window.electronAPI = api
}
// src/renderer/src/types/global.d.ts
export interface ElectronAPI {
getVersion: () => Promise<string>
openFile: (filePath: string) => Promise<string[] | null>
}
declare global {
interface Window {
electronAPI: ElectronAPI
}
}
打开 `contextIsolation`、关掉 `nodeIntegration`,是 Electron 官方对新项目的默认建议。哪怕是纯内部工具,也建议从第一天就按这个规范写,省得后期重构。
三、打包配置:electron-builder
真正决定产物形态的是 `electron-builder`。一个可用的最小配置:
{
"appId": "com.example.desktop",
"productName": "示例桌面应用",
"directories": { "output": "release" },
"files": ["dist/**/*", "resources/**/*"],
"win": {
"target": [{ "target": "nsis", "arch": ["x64"] }],
"icon": "build/icon.ico"
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true,
"perMachine": false,
"createDesktopShortcut": true
}
}
几个经验值:
• `oneClick: false` 会生成带安装向导的版本,用户可以选择安装路径。国内环境里这比免安装单文件友好得多。
• `files` 数组务必用白名单,只打进 `dist` 和静态资源。默认行为会把整个项目目录塞进去,`node_modules`、源码、测试文件全进包,体积能到几百 MB。
• 图标必须是多尺寸 ICO。直接拿一张 256×256 的 PNG 塞进去,Windows 任务栏会显示成模糊的色块。
• `asar: true` 是默认值,不要随手关掉。关掉后 `app.asar` 变成明文目录,代码直接可读,不利于保护。
四、几个真实踩过的坑
**1. 打包后白屏,开发时一切正常**
这是最常见的问题。原因是开发环境由 Vite 提供页面,打包后 `file://` 协议下的资源路径解析方式不同。解决方式是把渲染进程的 base 设为相对路径:
renderer: {
base: './',
...
}
同时主进程加载窗口时用 `loadFile` 而不是 `loadURL`:
win.loadFile(path.join(__dirname, '../renderer/index.html'))
**2. 动态 import 的原生模块在打包后找不到**
如果项目里用了 `better-sqlite3`、`sharp` 这类含原生二进制的依赖,除了 electron-builder 的 rebuild,还需要在打包配置里显式声明:
"asarUnpack": ["**/node_modules/better-sqlite3/**"]
否则运行时会报 `Cannot find module`。
**3. Electron 版本与依赖的 Node ABI 不匹配**
表现是启动即崩,日志里是一堆 `NODE_MODULE_VERSION` 不匹配。解决办法是固定 Electron 版本,并在 `postinstall` 里加一句 `electron-builder install-app-deps`,让原生依赖按当前 Electron 的 ABI 重新编译。
**4. Windows 上杀不掉进程**
单实例应用的实现里,第二次启动应该把信号传给主窗口而不是新开一个。Windows 上 `app.requestSingleInstanceLock()` 配合 `second-instance` 事件,比自己写锁文件可靠得多。
五、产出物与分发
`electron-builder` 会在 `release` 目录产出三类文件:未签名的安装包、免安装的绿色版目录、以及 `latest.yml`(用于 `electron-updater` 的差分更新)。
如果打算做自动更新,`latest.yml` 必须和安装包一起上传到更新服务器,且 `publish` 配置指向该地址:
"publish": [{ "provider": "generic", "url": "https://example.com/releases" }]
自动更新包必须做代码签名,否则 Windows SmartScreen 会直接拦截,升级体验会非常差。没有签名证书时,至少要在 `nsis` 里配置 `perMachine: false` 并跳过高权限请求,减少被杀软误报的概率。
六、小结
把 Electron 项目交付出去,核心就三件事:构建目标分清楚(主进程 Node、渲染进程浏览器)、依赖外置加白名单、原生依赖按目标 ABI 重编译。剩下的坑基本都集中在路径解析和权限声明上。
工具链本身很成熟,`electron-vite` + `electron-builder` 这套组合已经能覆盖绝大多数中小型桌面应用的发布需求。与其自己拼 webpack 配置,不如把这套经过大量项目验证的脚手架用熟,把时间花在业务本身上。
- 点赞
- 收藏
- 关注作者
评论(0)