Uninote
Uninote
用户根目录
brdr
common
programming
docs
tests
后端试题
问题讨论

Electron 进程通信总结

一、为什么需要 IPC

Electron 有两个进程:

进程 职责 能否直接访问
主进程 (main) 创建窗口、系统 API、文件、菜单 Node.js 全部能力
渲染进程 (renderer) 网页 UI 默认只有浏览器环境,无 Node

两者内存隔离,不能直接调用对方函数,必须通过 IPC(进程间通信)。


二、四种基本模式

模式 主进程 渲染进程 是否带返回值
渲染 → 主(单向) ipcMain.on(ch, cb) ipcRenderer.send(ch, ...args) ❌
渲染 → 主(请求/响应) ipcMain.handle(ch, cb) ipcRenderer.invoke(ch, ...args) ✅ Promise
主 → 渲染(单向) webContents.send(ch, ...args) ipcRenderer.on(ch, cb) ❌
主 → 渲染(请求/响应) 无内置,需两个频道自己实现 无内置 —

记忆口诀

  • 渲染要问主进程 → invoke / handle(最常用,有返回)
  • 主进程要通知渲染 → webContents.send / on
  • 渲染只上报不等待 → send / on

unknown Record

三、方向速查图

        ┌─────────────────────────────────────────┐
        │              主进程 main                 │
        │  ipcMain.on / ipcMain.handle            │
        │  webContents.send                       │
        └───────▲──────────────────────┬──────────┘
                │                      │
   invoke/send  │                      │  webContents.send
   (渲染→主)    │                      │  (主→渲染)
                │                      ▼
        ┌───────┴──────────────────────┴──────────┐
        │           渲染进程 renderer              │
        │  ipcRenderer.invoke / send / on         │
        └─────────────────────────────────────────┘

四、两种方向的完整写法

1. 渲染 → 主(有返回)

// 主进程
ipcMain.handle('user:get', async (event, id) => {
  return await db.findUser(id);   // 返回值会作为 Promise resolve
});
// 渲染进程
const user = await ipcRenderer.invoke('user:get', 123);

2. 主 → 渲染(单向推送)

// 主进程
win.webContents.send('download:progress', { percent: 42 });
// 渲染进程
ipcRenderer.on('download:progress', (event, data) => {
  console.log(data.percent);
});

3. 主 → 渲染,渲染要回复(自己拼)

// 主进程:发请求并等回复
let seq = 0;
const pending = new Map();

function ask(win, channel, payload) {
  const id = ++seq;
  return new Promise((resolve) => {
    pending.set(id, resolve);
    win.webContents.send(channel, { id, ...payload });
  });
}
ipcMain.on('reply:channel', (e, { id, data }) => {
  pending.get(id)?.(data);
  pending.delete(id);
});
// 渲染进程
ipcRenderer.on('request:channel', (e, { id, ...payload }) => {
  const result = doSomething(payload);
  ipcRenderer.send('reply:channel', { id, data: result });
});

你代码里的 quick-cmd-form:action + quick-cmd-form:result 就是这个模式。


五、安全:contextBridge + preload

不要在渲染进程直接暴露 ipcRenderer:

// ❌ 危险
contextBridge.exposeInMainWorld('ipc', ipcRenderer);

正确做法:preload 里只暴露白名单方法。

// preload.js
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('api', {
  getUsers: () => ipcRenderer.invoke('user:list'),
  onProgress: (cb) => {
    const listener = (_e, data) => cb(data);
    ipcRenderer.on('download:progress', listener);
    return () => ipcRenderer.removeListener('download:progress', listener);
  }
});
// 渲染进程
const users = await window.api.getUsers();
const off = window.api.onProgress((p) => console.log(p));
// 不用时 off();

要点:

  • 只暴露具体方法,不暴露 ipcRenderer 本体。
  • 主进程侧用 authorized(event) 校验来源窗口(你代码里已有)。
  • 频道名、参数类型都要校验。

六、你项目里的 IPC 全景

以 quick-cmd-form 为例:

频道 方向 模式
quick-cmd-form:show 主窗口 → 主进程 invoke / handle
quick-cmd-form:execute 表单 → 主进程 invoke / handle
quick-cmd-form:hide 表单 → 主进程 invoke / handle
quick-cmd-form:snippet-mode 表单 → 主进程 invoke / handle
quick-cmd-form:result 表单 → 主进程 send / on
quick-cmd-form:action 主进程 → 表单 webContents.send / on
quick-cmd-form:progress 主进程 → 表单 webContents.send / on
quick-cmd-form:reset 主进程 → 表单 webContents.send / on
quick-cmd-form:key 主进程 → 表单 webContents.send / on
quick-cmd-form:clear-console 主进程 → 所有窗口 webContents.send 广播

规律:

  • 渲染主动要结果 → invoke / handle(execute、hide、show)
  • 主进程主动通知 → webContents.send(progress、reset、key、action)
  • 表单回传结果但不需要等待 → send / on(result)

七、常见坑

坑 解决
窗口销毁后 send 报错 if (!win.isDestroyed()) win.webContents.send(...)
页面没加载完就 send,消息丢失 等 did-finish-load,或渲染侧主动 invoke 拉取
on 重复注册,回调触发多次 保存 listener 引用,用 removeListener 清理
传了不可序列化对象(函数、DOM) 只传普通对象/数组/字符串/数字/Buffer
任意窗口都能调敏感 channel authorized(event) 校验 event.sender
直接暴露 ipcRenderer 用 contextBridge 白名单封装

八、一句话总结

Electron IPC 就四件事:

  1. 渲染问主进程 → invoke / handle(有返回,最常用)
  2. 渲染通知主进程 → send / on(不等待)
  3. 主进程通知渲染 → webContents.send / on(单向推送)
  4. 主进程要渲染回复 → 两个频道 + id 配对,自己实现请求/响应

所有通信都要经过 preload + contextBridge 白名单 暴露,主进程侧用 authorized(event) 校验来源,才能保证安全。

misc

openDevTools

  const inputWindow = new BrowserWindow({ width: 800, height: 600 });
  inputWindow.loadFile('input.html');
  inputWindow.webContents.openDevTools();

ds-test

flutter

点赞(0) 阅读(7) 举报
目录
标题