2022 年 Electron 中从主进程向渲染器发送消息

Cle*_*kel 6 ipc electron

在我的应用程序中,我有两个窗口:mainWindowactionWindow在我的 mainWindow 上,当 actionWindow 关闭时,我使用侦听器从主进程接收消息。ipcRenderer.on然而,该消息没有通过。

用于mainWindow控制在 上发生的操作actionWindow(例如导航到 URL、远程关闭窗口……)。我想赋予用户手动移动和关闭的权力actionWindow,这就是为什么它的标题栏可见且可用。

我通过预加载文件中的 contextBridgeipcRenderer.invoke公开双向通信并公开给 mainWindow 的渲染器。ipcRenderer.on

这就是代码的样子(基于vite-electron-builder 模板

主要流程

const mainWindow = new BrowserWindow({
  show: false, // Use 'ready-to-show' event to show window
  webPreferences: {
    nativeWindowOpen: true,
    webviewTag: false,
    preload: join(__dirname, "../../preload/dist/index.cjs"),
  },
});
const actionWindow = new BrowserWindow({
  // some props
})
actionWindow.on("close", () => {
  console.log("window closed")
  mainWindow.webContents.send("closed", { message: "window closed" });
});

Run Code Online (Sandbox Code Playgroud)

预载

contextBridge.exposeInMainWorld("ipcRenderer", {
  invoke: ipcRenderer.invoke,
  on: ipcRenderer.on,
});
Run Code Online (Sandbox Code Playgroud)

渲染器(主窗口)

window.ipcRenderer.on("closed", () => {
  console.log("message received")
  // do something
  });
Run Code Online (Sandbox Code Playgroud)

我知道一个事实

  • mainWindow 可以访问公开的侦听器,因为invoke工作及其在主进程上触发的操作按预期在 actionWindow 上执行+响应也返回到渲染器。
  • actionWindow 上的侦听器可以正常工作,因为我可以在控制台中close看到日志window closed
  • message received没有出现在我的开发工具控制台中

对我来说这意味着

  • mainWindow.webContents.send不起作用 -> 消息从未发送
  • window.ipcRenderer.on不起作用 -> 消息永远不会到达目的地

因此,要么我的代码有问题,要么 Electron 最近对其中一种我不知道的方法施加了一些限制。

有任何想法吗?如果有比 IPC 更智能的方法来做到这一点,我也愿意这样做。

Cle*_*kel 4

好吧,经过几个小时的搜索、尝试和痛苦,我(几乎是意外地)找到了解决我的问题的方法。on当您从渲染器调用该方法时,电子似乎不再执行任何操作。再次研究有关 contextBridge 的文档,invoke我发现我暴露给on渲染器的方式被认为是糟糕的代码。更安全的方法是为您想要使用的每个 ipc 通道公开一个函数。在我使用 TypeScript 的情况下,它看起来像这样:

预载

contextBridge.exposeInMainWorld("ipcRenderer", {
  invokeOpen: async (optionsString: string) => {
    await ipcRenderer.invoke("open", optionsString);
  },
  onClose: (callback: () => void) => {
    ipcRenderer.on("closed", callback);
  },
  removeOnClose: (callback: () => void) => {
    ipcRenderer.removeListener("closed", callback);
  },
});
Run Code Online (Sandbox Code Playgroud)

渲染器(主窗口)

window.ipcRenderer.onClose(() => {
  // do sth
});
window.ipcRenderer.invokeOpen(JSON.stringify(someData)).then(() => {
  // do sth when response came back
});

Run Code Online (Sandbox Code Playgroud)

注意:为了通过在主窗口的每个渲染上创建侦听器来防止内存泄漏,您还必须使用提供的清理功能removeOnClose(请参阅预加载)。根据前端框架的不同,此功能的使用方法也有所不同。使用 React 看起来像这样:

const doSth= () => {
    console.log("doing something")
    ...
  };

  useEffect(() => {
    window.ipcRenderer.onClose(doSth);
    return () => {
      window.ipcRenderer.removeOnClose(doSth);
    };
  }, []);
Run Code Online (Sandbox Code Playgroud)

这不仅是一个更安全的解决方案,而且它实际上突然起作用了:O 使用清理功能我们还可以处理泄漏。