第 4 章 Electron:老牌桌面方案
VS Code、Slack、Discord、Figma 都是 Electron 写的。它的思路是把 Chromium 和 Node.js 一起打包,你用纯 Web 技术写 UI,还能直接调 Node API。优点是生态成熟、不用学 Rust;缺点是包大、内存高。
主进程 / 渲染进程 / IPC
程Electron 的三层结构
主进程(main):Node.js 环境,一个 App 只有一个。管窗口生命周期、菜单、托盘、文件系统、系统 API。
渲染进程(renderer):Chromium 环境,每个窗口一个,跑你的前端页面。
preload 脚本:桥接层。通过 contextBridge 把主进程安全暴露的 API 给渲染进程用。不要在渲染进程直接开 Node 集成,安全风险大。
IPC:主进程和渲染进程通信的唯一通道。ipcMain.handle 接收,ipcRenderer.invoke 调用。
contextIsolation: true(隔离上下文)、nodeIntegration: false(渲染进程不能直接用 Node)、sandbox: true(沙箱)、配 CSP 内容安全策略。这几个开了,Electron 才是安全的。从网上抄老教程经常看到关闭这些选项,别照抄。
主进程 main.js 骨架
preload.js —— 安全地暴露 API 给前端
打包:electron-builder
IPC 两种模式:invoke/handle vs send/on
Electron 就是把网页包装成桌面 APP。主进程(Node 环境,一个 App 一个)管窗口/菜单/托盘/系统 API;渲染进程(Chromium,每窗口一个)跑你的 React 页面。两者唯一通道是 IPC。
原生能力全家桶
| 模块 | 能干啥 |
|---|---|
| Menu | 应用菜单、右键上下文菜单、快捷键 accelerator。 |
| Tray | 系统托盘图标、tooltip、菜单、点击/闪烁。 |
| globalShortcut | 全局快捷键(App 在后台也能触发)。 |
| dialog | showOpenDialog/showSaveDialog/showMessageBox 文件选择、弹窗。 |
| Notification | 系统通知。 |
| clipboard | 读写文本/图片到剪贴板。 |
| screen | 屏幕尺寸、缩放比。 |
| shell | 用系统默认浏览器打开链接、显示文件。 |
| autoUpdater | 配合 electron-updater 检查/下载/安装更新。 |
安全铁律与打包分发
contextIsolation: true(隔离渲染与 preload)、nodeIntegration: false(渲染进程不能直接 require)、sandbox: true(沙箱)、配 CSP。preload 只通过 contextBridge 暴露最小 API,绝不把 ipcRenderer 整个甩出去。照老教程把 nodeIntegration 打开,XSS 一来直接接管你电脑。
electron-builder 打包配置要点
完整案例:Electron + React + Vite + TS 写 Markdown 编辑器——主进程 BrowserWindow + preload 暴露 openFile/saveFile(走 dialog + fs),渲染进程 React 编辑器,托盘图标,electron-updater 自动更新。包体优化用 asar 打包 + 代码分割 + 移除 devDependencies。
Q1. 主进程和渲染进程有什么区别?为什么不能在渲染进程直接 require Node?
看答案
主进程是 Node 环境、管窗口和系统 API;渲染进程是 Chromium、跑 UI。渲染进程加载的网页不可信(XSS/第三方内容),直接给它 Node 权限等于把系统命令交出去,必须 nodeIntegration:false + preload contextBridge 白名单暴露。
Q2. preload 脚本起什么作用?
看答案
桥接主进程和渲染进程,在 contextIsolation 开启时通过 contextBridge.exposeInMainWorld 把主进程白名单 API 安全挂到 window 上,前端只能用这些显式暴露的方法。
Q3. ipcMain.handle/invoke 和 send/on 怎么选?
看答案
需要返回值用 handle/invoke(请求-响应、Promise);主进程主动单向推送用 send/on。