进程间通信 (IPC)
Electron - 进程间通信 (IPC)
Section titled “Electron - 进程间通信 (IPC)”Electron 应用至少有两种类型的进程:一个主进程和一个或多个渲染进程。进程间通信 (IPC) 对于这些进程之间交换信息至关重要。
Electron 提供了用于 IPC 的模块:ipcMain(用于主进程)和 ipcRenderer(用于渲染进程)。它们允许异步通信,在某些情况下也允许同步通信。
在主进程中使用的 ipcMain 模块处理从渲染进程发送的消息。它可以回复这些消息,或主动与渲染器通信。
在预加载脚本或渲染进程中(当启用 Node 集成时,尽管出于安全原因不推荐)使用的 ipcRenderer 模块向主进程发送消息并处理回复。
现代 Electron 开发强调安全性。这意味着 nodeIntegration 通常被禁用,并且在渲染进程中启用了 contextIsolation。与主进程的通信通过使用 contextBridge 的 preload 脚本安全地进行。
让我们创建一个示例,演示异步、同步(带有注意事项)和 invoke/handle(请求/响应)IPC 模式。
首先,创建你的主进程文件 main.js:
const { app, BrowserWindow, ipcMain } = require('electron');const path = require('path');
let mainWindow;
function createWindow() { mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { preload: path.join(__dirname, 'preload.js'), contextIsolation: true, // 防止原型污染 nodeIntegration: false, // 在渲染器中禁用 Node.js 集成 } });
mainWindow.loadFile(path.join(__dirname, 'index.html')); // mainWindow.webContents.openDevTools(); // 可选:打开 DevTools}
// 处理来自渲染器的异步消息ipcMain.on('asynchronous-message', (event, arg) => { console.log('Main process received async:', arg); // 输出 'async ping' // 回复发送者(渲染进程) event.reply('asynchronous-reply', 'async pong from main');});
// 处理来自渲染器的同步消息// 注意:除非绝对必要,否则应避免同步 IPC 以提高性能。ipcMain.on('synchronous-message', (event, arg) => { console.log('Main process received sync:', arg); // 输出 'sync ping' event.returnValue = 'sync pong from main';});
// 用于 invoke/handle 模式的处理器(请求/响应)ipcMain.handle('invoke-message', async (event, arg) => { console.log('Main process received invoke:', arg); // 输出 'invoke ping' // 如果需要,执行一些异步操作 // const result = await somePromise(); return 'pong from handle in main';});
app.whenReady().then(() => { createWindow();
app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow(); } });});
app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit(); }});接下来,在同一目录下创建一个 preload.js 文件。此脚本在渲染进程中的网页加载之前运行,并可以访问 Node.js API。它安全地将 IPC 功能暴露给渲染器:
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', { send: (channel, data) => ipcRenderer.send(channel, data), sendSync: (channel, data) => ipcRenderer.sendSync(channel, data), on: (channel, func) => { // 特意移除 event,因为它包含 `sender` ipcRenderer.on(channel, (event, ...args) => func(...args)); }, invoke: (channel, data) => ipcRenderer.invoke(channel, data), // 最好明确暴露哪些通道 // 例如:openFile: () => ipcRenderer.invoke('dialog:openFile')});现在,创建你的 index.html 文件:
<!DOCTYPE html><html><head> <meta charset="UTF-8"> <title>Electron IPC Demo</title></head><body> <h1>Electron IPC Communication</h1> <p>Check the console in both this window (Renderer) and your terminal (Main).</p>
<script> // 1. 异步 IPC:发送并监听回复 console.log('Renderer: Sending async message...'); window.electronAPI.send('asynchronous-message', 'async ping from renderer');
window.electronAPI.on('asynchronous-reply', (reply) => { console.log('Renderer received async reply:', reply); // 'async pong from main' });
// 2. 同步 IPC:发送并获取即时结果 // 警告:同步 IPC 可能会阻塞渲染进程。请谨慎使用。 console.log('Renderer: Sending sync message...'); const syncReply = window.electronAPI.sendSync('synchronous-message', 'sync ping from renderer'); console.log('Renderer received sync reply:', syncReply); // 'sync pong from main'
// 3. Invoke/Handle IPC:请求/响应模式(基于 Promise) async function testInvoke() { console.log('Renderer: Sending invoke message...'); const invokeReply = await window.electronAPI.invoke('invoke-message', 'invoke ping from renderer'); console.log('Renderer received invoke reply:', invokeReply); // 'pong from handle in main' } testInvoke(); </script></body></html>要运行此应用,请确保你的 package.json 文件包含 "main": "main.js",并且有一个启动脚本,例如 "start": "electron ."。然后运行:
npm start你的应用 DevTools 控制台(渲染器)的输出:
Renderer: Sending async message...Renderer: Sending sync message...Renderer received sync reply: sync pong from mainRenderer: Sending invoke message...Renderer received async reply: async pong from mainRenderer received invoke reply: pong from handle in main你的终端控制台(主进程)的输出:
Main process received async: async ping from rendererMain process received sync: sync ping from rendererMain process received invoke: invoke ping from renderer要点总结:
- 始终优先使用异步 IPC (
send/on或invoke/handle),以避免阻塞进程并保持 UI 响应。 - 当启用
contextIsolation(默认且推荐)时,使用带有contextBridge的preload脚本安全地向渲染进程暴露 IPC 通道和 Node.js 功能。 invoke/handle模式非常适合请求-响应类型的交互,并返回一个 Promise。- 避免使用
remote模块,因为它已被废弃且存在安全隐患。