Electron IPC 是什么?
· 6 min read
Electron 的本质是两个不同类型的进程,IPC 是它们之间唯一合法沟通的桥。
- 进程模型:一个 main 进程(Node.js 全权限)+ N 个 renderer 进程(Chromium 受限沙箱)
- IPC 核心:main 用
ipcMain监听,renderer 用ipcRenderer发起,contextBridge是安全转交 - 三种调用模式:
send/on(单向)、invoke/handle(异步返回值)、MessagePort(双向长连接) - 安全铁律:默认开
contextIsolation+nodeIntegration: false,主进程做权限守门人 - 反模式:在 renderer 里直接
require('fs')、把 token 放 IPC payload、不校验 sender
Electron 项目最容易出问题的地方不是 UI、不是打包,是 IPC。这一层如果搞错,要么是页面调不到 Node API,要么是把整个文件系统暴露给渲染进程,被 XSS 一键拖库。
这篇文章把 Electron IPC 一次讲透,从进程模型到安全配置。
一、Electron 为什么要拆成多个进程?
Electron 不是单个程序,至少由两类进程组成:
| 进程 | 数量 | 运行时 | 权限 | 职责 |
|---|---|---|---|---|
| main 进程 | 1 个 | Node.js | 全 | 创建窗口、菜单、系统托盘、文件 IO、子进程 |
| renderer 进程 | N 个(每窗口一个) | Chromium | 受限 | 渲染 HTML/JS/CSS、处理用户交互 |
renderer 进程默认不能直接访问 Node.js API。这是 Chromium 多进程架构的硬规则——每个 tab 跑在独立沙箱里,挂掉一个不影响其他。Electron 在此基础上让 renderer 也能加载本地资源(file://),但保留 Node 隔离。
那 renderer 想读文件、调用系统 API 怎么办?只能通过 IPC 找 main 进程代办。
二、IPC 是什么?
IPC(Inter-Process Communication) 就是 main 和 renderer 之间的消息通道。
类比一下:
- renderer 像浏览器里的前端页面
- main 像后端 API 服务
- IPC 就是它们之间的 HTTP
Electron 提供两种 API:
// main 进程(src/main.ts)
import { ipcMain } from 'electron';
ipcMain.handle('read-config', async () => {
return await fs.readFile('config.json', 'utf-8');
});
// renderer 进程(src/preload.ts 通过 contextBridge 暴露)
import { contextBridge, ipcRenderer } from 'electron';
contextBridge.exposeInMainWorld('api', {
readConfig: () => ipcRenderer.invoke('read-config'),
});
// renderer 页面里直接用
const config = await window.api.readConfig();
三个角色各司其职:
- ipcMain:main 进程侧的消息接收器
- ipcRenderer:renderer 进程侧的消息发起器
- contextBridge:在 preload 脚本里把 ipcRenderer 方法白名单化给页面用
三、三种 IPC 调用模式
模式 1:send + on(单向,无返回值)
适合通知类消息("窗口已创建"、"开始下载"):
// renderer → main
ipcRenderer.send('log', '用户登录了');
// main
ipcMain.on('log', (event, msg) => console.log(msg));
send 是单向的,main 没办法直接回值给 renderer。
模式 2:invoke + handle(异步双向,最常用)
renderer 发起请求,main 返回 Promise:
// main
ipcMain.handle('save-file', async (_e, path, content) => {
await fs.writeFile(path, content);
return { ok: true };
});
// renderer
const result = await window.api.saveFile('/tmp/a.txt', 'hello');
这是 90% 场景的首选。
模式 3:MessagePort(建立长连接)
需要高频双向通信(实时数据流、长任务进度推送)时用:
// main 创建 port
const { port1, port2 } = new MessageChannelMain();
mainWindow.webContents.postMessage('init-port', null, [port2]);
// port1 留在 main 监听
port1.on('message', (e) => console.log(e.data));