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',...)

目录

  1. ipcRenderer.send
  2. ipcRenderer.invokeipcMain.handle
  3. ipcRenderer.sendSync
  4. ipcRenderer.on / once / 移除监听
  5. ipcRenderer.postMessage(MessagePort 通道)
  6. ipcRenderer.sendTo
  7. ipcRenderer.sendToHost
  8. 主进程侧常用补充:webContents.sendipcMain.handleOnce/once
  9. 最佳实践与安全清单(强烈推荐)
  10. 常见问题(FAQ)与排错

1) ipcRenderer.send(channel, ...args)

用途:渲染器 → 主进程的异步单向消息。若需要回执,由主进程主动再发一条(event.replywebContents.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.invokeipcMain.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) 最佳实践与安全清单

  1. 启用隔离: contextIsolation: truenodeIntegration: false

  2. 使用 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);
      }
    });
    
  3. 避免 sendSync

  4. 大数据传输用 postMessage

  5. 移除监听,避免内存泄漏。


10) 常见问题(FAQ)

  • invoke 没回? 确认主进程用了 ipcMain.handle 而不是 on
  • sendSync 卡住? 必须同步设置 event.returnValue
  • 跨窗口通信? 主进程分发 webContents.id,或由主进程做消息路由。
  • 内存上涨? 忘记 removeListener

小结

  • 请求-应答invoke/handle
  • 推送/广播webContents.send + on/once
  • 高吞吐postMessage + MessagePort
  • 跨渲染器sendTo
  • 避免阻塞:尽量不用 sendSync

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐