Skip to content

进程间通信 (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 main
Renderer: Sending invoke message...
Renderer received async reply: async pong from main
Renderer received invoke reply: pong from handle in main

你的终端控制台(主进程)的输出:

Main process received async: async ping from renderer
Main process received sync: sync ping from renderer
Main process received invoke: invoke ping from renderer

要点总结:

  • 始终优先使用异步 IPC (send/on 或 invoke/handle),以避免阻塞进程并保持 UI 响应。
  • 当启用 contextIsolation(默认且推荐)时,使用带有 contextBridge 的 preload 脚本安全地向渲染进程暴露 IPC 通道和 Node.js 功能。
  • invoke/handle 模式非常适合请求-响应类型的交互,并返回一个 Promise。
  • 避免使用 remote 模块,因为它已被废弃且存在安全隐患。

更多详细信息,请查阅 Electron 官方关于 进程间通信 和 预加载脚本 的文档。