在Electron中直接在两个渲染进程之间进行通信

9

我在Electron的主进程中创建了多个窗口,需要在它们之间传递消息。我所知道的从rendererA发送消息到rendererB的唯一方法是将其反弹到主进程。是否有直接从rendererA发送消息到rendererB的方法?


据我所知,Electron 的 IPC 能力不是很清楚。 - Alexander Leithner
3个回答

13

无论如何,主进程都必须参与其中,但在两个窗口的渲染器进程之间进行通信可以以某种简单直接的方式实现:

  • 在主进程中,将窗口引用定义为全局对象的属性;

  • 在每个渲染器进程中,通过使用 remote.getGlobal() 访问要发送消息的窗口的引用,然后使用 send() 方法;

  • 像往常一样使用 ipcRenderer.on() 在每个渲染器进程中接收消息。

这里有一个Electron 应用的示例,它就是这样实现的:

main.js

const { app, BrowserWindow } = require ('electron');
global.window1 = null;
global.window2 = null;
function onAppReady ()
{
    window1 = new BrowserWindow ({ width: 600, height: 500 });
    window1.loadURL (`file://${__dirname}/index1.html`);
    window1.webContents.openDevTools ();
    window1.on ('closed', () => { window1 = null; });
    //
    window2 = new BrowserWindow ({ width: 500, height: 600 });
    window2.loadURL (`file://${__dirname}/index2.html`);
    window2.webContents.openDevTools ();
    window2.on ('closed', () => { window2 = null; });
}
app.on ('ready', onAppReady);
app.on ('window-all-closed', () => { app.quit (); });

index1.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Window 1</title>
  </head>
  <body>
    <h1>Window 1</h1>
    <button type="button" class="send-message">Send Message to Window 2</button>
    <script>
        const { remote, ipcRenderer } = require ('electron');
        //
        let button = document.querySelector ('.send-message');
        button.addEventListener ('click', () =>
        {
            let window2 = remote.getGlobal ('window2');
            if (window2) window2.webContents.send ('message', "Message from Window 1");
        });
        //
        ipcRenderer.on ('message', (event, message) => { console.log (message); });
    </script>
  </body>
</html>

index2.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Window 2</title>
  </head>
  <body>
    <h1>Window 2</h1>
    <button type="button" class="send-message">Send Message to Window 1</button>
    <script>
        const { remote, ipcRenderer } = require ('electron');
        //
        let button = document.querySelector ('.send-message');
        button.addEventListener ('click', () =>
        {
            let window1 = remote.getGlobal ('window1');
            if (window1) window1.webContents.send ('message', "Message from Window 2");
        });
        //
        ipcRenderer.on ('message', (event, message) => { console.log (message); });
    </script>
  </body>
</html>

1
我刚刚在我的Electron应用程序中测试了你的“直接消息流”代码,其中包括一个主窗口和一个隐藏的工作渲染器进程窗口,其中工作进程向主窗口发送了大量消息。它的表现很好,但速度比以前慢得多(约5倍)。之前我将所有消息发送到主进程,然后再将它们分派到主渲染器窗口。我猜这与let window2 = remote.getGlobal('window2');有关。 - Mike Lieser

12

基本上,在Electron中,进程间通信有三种形式:

  1. 主进程 -> 渲染进程:发送者使用webContents.fromId(id).send(),接收者使用ipcRenderer.on
  2. 渲染进程 -> 主进程:发送者使用ipcRenderer.send(),接收者使用ipcMain.on
  3. 渲染进程 -> 渲染进程:发送者使用ipcRenderer.sendTo(),接收者使用ipcRenderer.on

因此,在渲染进程到渲染进程的场景中,发送者必须知道目标的webContents.id,然后通过ipcRenderer.sendTo()进行调用。


我制作了一个Electron IPC框架,electron-ipcfy,它统一了以上三种情况下的IPC调用。

import { ipcfy } from "electron-ipcfy";

interface TestService {
    greet(name: string);
}

const testService = ipcfy<TestService>('test');

if (process.type == 'browser') {
    // Attach implementation
    testService.__attachImpl(
        new class implements TestService {
            greet(name: string) {
                console.log(`Hello, ${name}!`);
            }
        });
}

// Then you can call it in any process
testService.greet('world');

sendTo 仍然会通过主进程吗? - Bruce Lin

1

这取决于您的通信系统的逻辑。

例如,如果您总是需要从BrowserWindow2发送数据到BrowserWindow4,则可以在BrowserWindow4中声明ipcMain,在BrowserWindow2中声明ipcRenderer

如果您需要从所有BrowserWindows发送到其他所有窗口,则建议您使用主进程并将消息分发到BrowserWindows(使用相关的ID

在您的消息接收器中:

ipcMain.on('asynchronous-message', (event, arg) => {
   //manage data
}

在你的消息发送器中:
ipcRenderer.send('asynchronous-message', message)

ipcMain文档

ipcRenderer文档


在 BrowserWindow4 中声明 ipcMain 是什么意思?你可以更具体一些吗? - hawk
我添加了一些信息,如果需要澄清,请问我 :) - emish89

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接