electron-通信
Electron IPC 通信 API 摘要 本文整理了 Electron 中 ipcRenderer 和 ipcMain 的完整 API 用法
·
Electron ipcRenderer
API 全整理(含 ipcMain
对应用法 + 示例)
适用对象:在 渲染进程 (renderer) 与 主进程 (main) 间进行 IPC 的 Electron 工程。
建议:开启contextIsolation: true
,通过preload
+contextBridge
暴露受控 API,避免在页面中直接使用ipcRenderer
。
顶部总览表(快速对照 & 迷你示例)
ipcRenderer API |
作用 & 行为 | 迷你示例(Renderer) | ipcMain 对应 |
迷你示例(Main) |
---|---|---|---|---|
send(channel, ...args) |
异步单向发送;不等返回 | ipcRenderer.send('ping', 1) |
on(channel, listener) |
ipcMain.on('ping',(e,n)=>e.reply('pong',n+1)) |
invoke(channel, ...args) |
基于 Promise 的请求-应答 | const r=await ipcRenderer.invoke('sum',[1,2]) |
handle(channel, handler) |
ipcMain.handle('sum',(_,a)=>a.reduce((x,y)=>x+y,0)) |
sendSync(channel, ...args) |
同步阻塞请求(谨慎使用) | const now=ipcRenderer.sendSync('now') |
on + event.returnValue |
ipcMain.on('now',(e)=>e.returnValue=Date.now()) |
on(channel, listener) |
监听主进程主动发来的消息 | ipcRenderer.on('tick',(_,t)=>...) |
webContents.send 主动推送 |
win.webContents.send('tick',Date.now()) |
once(channel, listener) |
仅触发一次 | ipcRenderer.once('ready',()=>...) |
once(channel, listener) |
ipcMain.once('boot',()=>...) |
removeListener(channel, fn) |
卸载某个回调 | ipcRenderer.removeListener('tick',fn) |
同名 | ipcMain.removeListener('ping',fn) |
removeAllListeners(channel?) |
卸载该频道全部回调 | ipcRenderer.removeAllListeners('tick') |
同名 | ipcMain.removeAllListeners('ping') |
postMessage(channel, message, [transfer]) |
结构化克隆 + 端口转移 | ipcRenderer.postMessage('port',{ok:1},[port2]) |
on 中通过 event.ports 接收 |
ipcMain.on('port',(e)=>e.ports[0].postMessage('hi')) |
sendTo(webContentsId, channel, ...args) |
发送到其他渲染器 | ipcRenderer.sendTo(id,'greet','hi') |
webContents.fromId(id).send(...) |
webContents.fromId(id)?.send('greet','hi') |
sendToHost(channel, ...args) |
发送到嵌入该页的 Host(如 BrowserView ) |
ipcRenderer.sendToHost('from-guest','data') |
由宿主统一转发 | view.webContents.on('ipc-message',...) |
目录
ipcRenderer.send
ipcRenderer.invoke
⇄ipcMain.handle
ipcRenderer.sendSync
ipcRenderer.on
/once
/ 移除监听ipcRenderer.postMessage
(MessagePort 通道)ipcRenderer.sendTo
ipcRenderer.sendToHost
- 主进程侧常用补充:
webContents.send
、ipcMain.handleOnce/once
- 最佳实践与安全清单(强烈推荐)
- 常见问题(FAQ)与排错
1) ipcRenderer.send(channel, ...args)
用途:渲染器 → 主进程的异步单向消息。若需要回执,由主进程主动再发一条(event.reply
或 webContents.send
)。
Renderer(渲染进程):
// renderer.js
const { ipcRenderer } = require('electron');
// 发起消息
ipcRenderer.send('app:ping', { t: Date.now() });
// 监听主进程回执
ipcRenderer.on('app:pong', (_event, data) => {
console.log('pong from main:', data);
});
Main(主进程):
// main.js
const { ipcMain, BrowserWindow } = require('electron');
ipcMain.on('app:ping', (event, payload) => {
console.log('got ping', payload);
// A) 就地回复给发送方
event.reply('app:pong', { ok: true, at: Date.now() });
// B) 任意时刻回复(例如异步完成后)
// event.sender.send('app:pong', { ok: true, at: Date.now() });
});
2) ipcRenderer.invoke
⇄ ipcMain.handle
用途:标准的 RPC/Promise 流程;支持 async/await
;主进程抛错将以 rejected Promise 传回渲染器。
Renderer:
// 计算求和
const ret = await ipcRenderer.invoke('calc:sum', [1, 2, 3]);
// 错误处理
try {
await ipcRenderer.invoke('secure:op', { token: 'abc' });
} catch (e) {
console.error('invoke failed:', e.message);
}
Main:
ipcMain.handle('calc:sum', (_event, nums) => {
return nums.reduce((a, b) => a + b, 0);
});
ipcMain.handle('secure:op', async (event, payload) => {
const wcId = event.sender.id;
if (!isTrusted(wcId, payload.token)) {
throw new Error('Unauthorized');
}
return { ok: true };
});
3) ipcRenderer.sendSync
用途:同步请求一个结果,调用期间会阻塞渲染线程 —— 不推荐在生产中使用。
Renderer:
const now = ipcRenderer.sendSync('sys:now');
console.log(now);
Main:
ipcMain.on('sys:now', (event) => {
event.returnValue = Date.now();
});
4) ipcRenderer.on
/ once
/ 移除监听
Renderer:
function onTick(_event, ts) {
console.log('tick at', ts);
}
ipcRenderer.on('app:tick', onTick);
ipcRenderer.once('app:ready', () => console.log('ready!'));
// 卸载监听
ipcRenderer.removeListener('app:tick', onTick);
Main:
const win = BrowserWindow.getAllWindows()[0];
setInterval(() => {
win.webContents.send('app:tick', Date.now());
}, 1000);
win.webContents.send('app:ready');
5) ipcRenderer.postMessage
(MessagePort 通道)
Renderer:
const { port1, port2 } = new MessageChannel();
ipcRenderer.postMessage('channel:open', { hello: 'main?' }, [port2]);
port1.onmessage = (ev) => console.log('from main:', ev.data);
port1.start();
Main:
ipcMain.on('channel:open', (event, payload) => {
console.log('renderer says:', payload);
const [port] = event.ports;
port.postMessage({ hello: 'renderer!' });
port.on('message', (ev) => console.log('via port:', ev.data));
port.start();
});
6) ipcRenderer.sendTo
Renderer(A → B):
ipcRenderer.sendTo(targetId, 'chat:message', { text: 'hi from A' });
接收方(B):
ipcRenderer.on('chat:message', (_e, msg) => {
console.log('got from A:', msg);
});
7) ipcRenderer.sendToHost
Renderer(被嵌入页):
ipcRenderer.sendToHost('guest:ready', { ok: true });
Host(主进程):
view.webContents.on('ipc-message', (_event, channel, ...args) => {
if (channel === 'guest:ready') {
console.log('guest says:', args);
}
});
8) 主进程侧常用补充
webContents.send(channel, ...args)
:主动给某个渲染器发送消息。ipcMain.handleOnce(channel, handler)
:仅处理一次的 RPC。ipcMain.once(channel, listener)
:仅监听一次的单向消息。
9) 最佳实践与安全清单
-
启用隔离:
contextIsolation: true
,nodeIntegration: false
。 -
使用
contextBridge
暴露 API:contextBridge.exposeInMainWorld('api', { ping: (n) => ipcRenderer.invoke('calc:sum', [n, 1]), onTick: (cb) => { const handler = (_e, ts) => cb(ts); ipcRenderer.on('app:tick', handler); return () => ipcRenderer.removeListener('app:tick', handler); } });
-
避免
sendSync
。 -
大数据传输用
postMessage
。 -
移除监听,避免内存泄漏。
10) 常见问题(FAQ)
invoke
没回? 确认主进程用了ipcMain.handle
而不是on
。sendSync
卡住? 必须同步设置event.returnValue
。- 跨窗口通信? 主进程分发
webContents.id
,或由主进程做消息路由。 - 内存上涨? 忘记
removeListener
。
小结
- 请求-应答:
invoke/handle
。 - 推送/广播:
webContents.send
+on/once
。 - 高吞吐:
postMessage
+MessagePort
。 - 跨渲染器:
sendTo
。 - 避免阻塞:尽量不用
sendSync
。
更多推荐
所有评论(0)