一、主进程 vs 渲染进程:核心对比
特性 | 主进程 (Main Process) | 渲染进程 (Renderer Process) |
---|---|---|
运行环境 | Node.js 环境 | Chromium 浏览器环境 |
数量 | 唯一(整个应用只有一个) | 多个(每个窗口对应一个) |
权限 | 可调用所有 Node.js API | 默认无法直接访问 Node.js(需 IPC 代理) |
典型职责 | 创建窗口、系统菜单、托盘图标、文件操作 | 渲染界面、处理用户交互、前端逻辑 |
生命周期 | 控制应用启动/退出 | 窗口关闭时自动销毁 |
二、IPC 通信机制详解
1. 通信模式示意图
csharp
[渲染进程] [主进程]
│ │
│ ipcRenderer.send('event', data) │
├─────────────────────────────────────>│
│ │
│ ipcMain.on('event', handler) │
│<─────────────────────────────────────┤
│ event.reply('reply-event', result) │
│ │
│ ipcRenderer.on('reply-event') │
│ │
2. 三种核心通信方式
方式 1:渲染进程 → 主进程(异步)
javascript
// 渲染进程(通过预加载脚本暴露的方法)
window.electronAPI.sendMessage('Hello Main!');
// 主进程
ipcMain.on('send-message', (event, data) => {
console.log(data); // 输出: Hello Main!
event.reply('reply-message', 'Received!');
});
// 渲染进程接收回复
window.electronAPI.onReply((data) => {
console.log(data); // 输出: Received!
});
方式 2:渲染进程 → 主进程(同步)
javascript
// 渲染进程(谨慎使用,会阻塞界面!)
const result = window.electronAPI.syncMessage('Sync request');
// 主进程
ipcMain.on('sync-message', (event, data) => {
event.returnValue = 'Sync response';
});
方式 3:主进程 → 渲染进程(主动推送)
javascript
// 主进程
const win = new BrowserWindow(...);
win.webContents.send('update-data', { value: 42 });
// 渲染进程
window.electronAPI.onUpdateData((data) => {
console.log(data.value); // 输出: 42
});
三、预加载脚本(Preload Script):安全桥梁
1. 核心作用图解
scss
[Node.js 环境] [隔离的浏览器环境]
主进程 <── IPC ──> 预加载脚本 <──> 渲染进程
(暴露有限 API)
2. 安全配置代码
javascript
// 创建窗口时配置
new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true, // 启用上下文隔离
nodeIntegration: false // 禁止直接访问 Node.js
}
});
// preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
sendMessage: (data) => ipcRenderer.send('send-message', data),
onReply: (callback) => ipcRenderer.on('reply-message', callback)
});
四、核心关系与通信场景
1. 典型场景示例
场景 1:文件读取
javascript
// 预加载脚本暴露方法
contextBridge.exposeInMainWorld('electronAPI', {
readFile: (path) => ipcRenderer.invoke('read-file', path)
});
// 主进程处理
ipcMain.handle('read-file', async (event, path) => {
return await fs.promises.readFile(path, 'utf-8');
});
// 渲染进程调用
const content = await window.electronAPI.readFile('data.txt');
场景 2:多窗口通信
javascript
// 主进程维护窗口引用
const wins = new Map();
ipcMain.on('create-window', (event, id) => {
const win = new BrowserWindow();
wins.set(id, win);
});
ipcMain.on('send-to-window', (event, id, message) => {
const targetWin = wins.get(id);
targetWin.webContents.send('receive-message', message);
});
五、关键安全原则
-
永远不要这样做 ❌:
html<!-- 禁用安全配置的危险写法 --> <webview nodeIntegration></webview> <script> require('child_process').exec('rm -rf /'); // 恶意代码可执行系统命令! </script>
运行 HTML
-
必须遵循的安全实践 ✅:
- 始终启用
contextIsolation: true
- 始终禁用
nodeIntegration: false
- 通过预加载脚本白名单式暴露 API
- 验证渲染进程发送的所有数据
- 始终启用
六、性能优化建议
-
减少 IPC 调用频率
合并多次操作为单次批处理:
javascript// 避免 for (let i = 0; i < 100; i++) { window.electronAPI.updateItem(i); } // 推荐 window.electronAPI.batchUpdate(items);
-
使用 SharedArrayBuffer 传递大数据
适用于需要高频更新的大量数据(如图像处理):
javascript// 主进程 const sharedBuffer = new SharedArrayBuffer(1024); win.webContents.send('shared-data', sharedBuffer); // 渲染进程 const sameBuffer = receivedBuffer;
七、总结:核心关系图谱
css
[主进程]
├── 通过 BrowserWindow 创建 → [渲染进程]
├── 通过 ipcMain 监听 ←→ ipcRenderer 发送(IPC 通信)
└── 通过预加载脚本 → 安全暴露 API
[渲染进程]
├── 通过 window.electronAPI 调用受限功能
└── 通过 webContents.send 接收主进程推送