IPC(Inter-Process Communication,进程间通信)是 Electron 架构的核心机制,用于在主进程和渲染进程之间传递消息和数据。
IPC 基础概念
Electron 的 IPC 模块包含两个主要部分:
- ipcMain: 在主进程中使用,用于接收来自渲染进程的消息
- ipcRenderer: 在渲染进程中使用,用于向主进程发送消息
基本通信方式
1. 渲染进程 → 主进程(单向通信)
发送消息(无响应)
javascript// 渲染进程 const { ipcRenderer } = require('electron') ipcRenderer.send('channel-name', { data: 'Hello from renderer' }) // 主进程 const { ipcMain } = require('electron') ipcMain.on('channel-name', (event, data) => { console.log('Received:', data) })
发送消息并等待响应
javascript// 渲染进程 ipcRenderer.invoke('channel-name', { data: 'Hello' }) .then(response => { console.log('Response:', response) }) // 主进程 ipcMain.handle('channel-name', async (event, data) => { // 处理逻辑 return { result: 'Success' } })
2. 主进程 → 渲染进程(单向通信)
javascript// 主进程 mainWindow.webContents.send('channel-name', { data: 'Hello from main' }) // 渲染进程 ipcRenderer.on('channel-name', (event, data) => { console.log('Received:', data) })
3. 双向通信
javascript// 渲染进程 ipcRenderer.send('request-data', { id: 123 }) ipcRenderer.on('response-data', (event, data) => { console.log('Response:', data) }) // 主进程 ipcMain.on('request-data', (event, data) => { // 处理请求 const result = processData(data) event.reply('response-data', result) })
高级通信模式
1. 使用 invoke/handle 进行异步通信
这种方式返回 Promise,更适合异步操作:
javascript// 渲染进程 async function fetchData() { try { const result = await ipcRenderer.invoke('fetch-data', { id: 1 }) return result } catch (error) { console.error('Error:', error) } } // 主进程 ipcMain.handle('fetch-data', async (event, { id }) => { // 可以执行异步操作 const data = await database.query(id) return data })
2. 使用 contextBridge 安全暴露 API
javascript// preload.js const { contextBridge, ipcRenderer } = require('electron') contextBridge.exposeInMainWorld('electronAPI', { // 发送消息 send: (channel, data) => ipcRenderer.send(channel, data), // 发送并等待响应 invoke: (channel, data) => ipcRenderer.invoke(channel, data), // 监听消息 on: (channel, callback) => { const subscription = (event, ...args) => callback(...args) ipcRenderer.on(channel, subscription) return () => ipcRenderer.removeListener(channel, subscription) }, // 移除监听器 removeListener: (channel, callback) => { ipcRenderer.removeListener(channel, callback) } }) // 渲染进程 window.electronAPI.send('channel-name', data) const response = await window.electronAPI.invoke('async-channel', data)
3. 使用 Event 进行窗口间通信
javascript// 主进程 - 从窗口 A 发送到窗口 B const windowA = BrowserWindow.fromWebContents(event.sender) const windowB = BrowserWindow.getAllWindows()[1] windowA.webContents.send('message-to-window-b', { data: 'Hello' }) // 窗口 B 的渲染进程 ipcRenderer.on('message-to-window-b', (event, data) => { console.log('Received from window A:', data) })
通信数据类型
IPC 支持传递的数据类型包括:
- 基本类型(String, Number, Boolean, null, undefined)
- 对象和数组
- Buffer
- Error 对象
- Date 对象
注意: 不能传递函数、DOM 节点、循环引用的对象
最佳实践
1. 使用明确的通道名称
javascript// 好的做法 ipcRenderer.send('user:login', credentials) ipcRenderer.send('file:read', filePath) // 不好的做法 ipcRenderer.send('message1', data) ipcRenderer.send('message2', data)
2. 错误处理
javascript// 渲染进程 try { const result = await ipcRenderer.invoke('async-operation', data) } catch (error) { console.error('Operation failed:', error) } // 主进程 ipcMain.handle('async-operation', async (event, data) => { try { const result = await process(data) return result } catch (error) { throw new Error(`Processing failed: ${error.message}`) } })
3. 清理监听器
javascript// 渲染进程 const cleanup = window.electronAPI.on('channel-name', (data) => { console.log(data) }) // 组件卸载时清理 componentWillUnmount() { cleanup() }
4. 使用 TypeScript 类型定义
typescript// types.ts interface IpcChannels { 'user:login': (credentials: Credentials) => Promise<User> 'file:read': (path: string) => Promise<string> } // preload.ts contextBridge.exposeInMainWorld('electronAPI', { invoke: <K extends keyof IpcChannels>( channel: K, ...args: Parameters<IpcChannels[K]> ): ReturnType<IpcChannels[K]> => ipcRenderer.invoke(channel, ...args) })
性能优化
1. 减少通信频率
javascript// 不好的做法 - 频繁通信 for (let i = 0; i < 1000; i++) { ipcRenderer.send('process-item', items[i]) } // 好的做法 - 批量处理 ipcRenderer.send('process-items', items)
2. 使用 Worker Threads 处理密集任务
javascript// 主进程 ipcMain.handle('heavy-computation', async (event, data) => { const worker = new Worker('./worker.js') return new Promise((resolve, reject) => { worker.on('message', resolve) worker.on('error', reject) worker.postMessage(data) }) })
安全考虑
- 验证输入数据: 在主进程中验证来自渲染进程的所有数据
- 限制通道访问: 只暴露必要的 IPC 通道
- 使用 contextIsolation: 确保渲染进程无法直接访问 Node.js API
- 避免敏感数据: 不要在 IPC 消息中传递敏感信息
常见问题
Q: invoke 和 send 有什么区别?A: invoke 返回 Promise,适合需要响应的场景;send 是单向通信,不返回结果。
Q: 如何在多个窗口之间通信?A: 通过主进程作为中介,使用 webContents.send() 向特定窗口发送消息。
Q: IPC 通信有大小限制吗?A: 理论上没有硬性限制,但建议传递大数据时使用文件系统或共享内存。