Electron 主进程和渲染进程通信

每个 Electron 应用都有一个单一的主进程,BrowserWindow 类的每个实例创建一个应用程序窗口,且在单独的渲染器进程中加载一个网页。本章我们来介绍下,如何在主进程和渲染器进程之间传递消息

渲染器向主进程发送消息

invoke / handle

渲染进程通过 invoke 向主进程发起异步调用,主进程通过 handle 处理并返回 promise

Electron 的 IPC 通信底层基于「通道名 - 数据」的映射机制,通道名是唯一标识通信逻辑的键

javascript 复制代码
ipcRenderer.invoke("renderer-invoke", message)
javascript 复制代码
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('node:path');
let window;
app.on('ready', () => {
    window = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            preload: path.join(__dirname, 'preload.js')
        }
    });
    window.loadFile('index.html');
});

ipcMain.handle("renderer-invoke", (event, message) => {
  console.log("main receive from renderer: ", message);
  return "hello world"
});

我们定义了一个 "renderer-invoke" 通道名,渲染进程通过这个通道名给主进程发送消息,主进程输出消息,并返回 "hello world"。

我们将 ipcRenderer.invoke 通过预加载脚本暴露给渲染进程。

javascript 复制代码
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('messageApi', {
    send :(message) => ipcRenderer.invoke("renderer-invoke", message)
});

渲染进程调用发送消息接口

javascript 复制代码
async function send () {
    const textContainer = document.getElementById('text-container');
    const result = await window.messageApi.send("hello");
    textContainer.textContent = result;
}
send()

html 显示

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>消息通信示例</title>
</head>
<body>
    <h1>Hello Electron!</h1>
    <div id="text-container"></div>
    <script src="./renderer.js"> </script>
</body>
</html>

npm start 运行,我们可以看到终端输出了,main receive from renderer: hello,界面上显示主进程返回的字符串 hello world。

send / on

send / on 用于单向通信,这里的单向指的是渲染进程不能通过像 invoke 那样获取主进程返回的结果,但是我们仍可以通过 event.reply 将结果发送给渲染进程。

同样的,我们调用 ipcRenderer.send 接口发送消息,使用预处理脚本暴露接口。

javascript 复制代码
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('messageApi', {
    sendMessage: (message) => ipcRenderer.send("renderer-main", message),
    onReceiveMessage: (callback) => {
        ipcRenderer.on("main-renderer", (_event, message) => {
            callback(message);
        });
    }
});

ipcRenderer.on 用于接收主进程的返回值,后面,渲染进程接收主进程发送的消息也是用同样的接口。

javascript 复制代码
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('node:path');
let window;
app.on('ready', () => {
    window = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            preload: path.join(__dirname, 'preload.js')
        }
    });
    window.loadFile('index.html');
ipcMain.on("renderer-main", (event, message) => {
  console.log("main receive from renderer", message);
  event.reply("main-renderer", "received");
});

主进程接收消息,并返回 received,其实从这里可以看出,send / on 的返回更像是主进程收到消息后,主动又发了一个新的消息。

javascript 复制代码
window.messageApi.onReceiveMessage((message) => {
    const textContainer = document.getElementById('text-container');
    textContainer.textContent = message;
});
window.messageApi.sendMessage("hello");

renderer 进程注册回调函数处理收到的消息,然后发送消息。

sendSync / on

ipcRenderer.sendSync API 向主进程发送消息,并同步 等待响应。它会阻塞渲染进程,直至主进程返回

预加载脚本暴露接口

javascript 复制代码
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('messageApi', {
    syncSendMessage: (message) => ipcRenderer.sendSync("synchronous-message", message)
});

主进程回调处理

javascript 复制代码
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('node:path');
let window;
app.on('ready', () => {
    window = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            preload: path.join(__dirname, 'preload.js')
        }
    });
    window.loadFile('index.html');
});

ipcMain.on("synchronous-message", (event, message) => {
  console.log("main receive from renderer", message);
  event.returnValue = "synchronous";
});

渲染进程发送消息,显示结果

javascript 复制代码
const textContainer = document.getElementById('text-container');
const result = window.messageApi.syncSendMessage("hello")
textContainer.textContent = result;

主进程向渲染进程发送消息

主进程可以通过 webContents.send api 主动向渲染进程发送消息

javascript 复制代码
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('node:path');
let window;
app.on('ready', () => {
    window = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            preload: path.join(__dirname, 'preload.js')
        }
    });
    window.loadFile('index.html');
    
    window.webContents.on('did-finish-load', () => {
        window.webContents.send("main-renderer", "hello");
    });
});

由于渲染进程可以有多个,所以不是像 ipcRenderer.send 那样发送,而是通过 BrowserWindow 的 webContents.send 发送消息,其他部分和渲染进程向主进程发送消息几乎一致。

javascript 复制代码
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('messageApi', {
    onReceiveMessage: (callback) => {
        ipcRenderer.on("main-renderer", (_event, message) => {
            callback(message);
        });
    }
});

预加载脚本暴露接口

cpp 复制代码
window.messageApi.onReceiveMessage((message) => {
    const textContainer = document.getElementById('text-container');
    textContainer.textContent = message;
});

渲染进程接收消息

渲染进程向渲染进程发送消息

ipcMain 和 ipcRenderer 模块无法在渲染进程之间发送消息,我们可以通过主进程进行中转,又或者通过 MessageChannelMain 模块来实现,这部分我们在之后的章节再详细介绍。

相关推荐
巴拉巴拉~~2 小时前
Flutter 通用滑块组件 CommonSliderWidget:单值 / 范围 + 刻度 + 标签 + 样式自定义
开发语言·前端·javascript
有意义3 小时前
从 useState 到 useEffect:React Hooks 核心机制详解
javascript·react.js·前端工程化
栀秋6663 小时前
面试常考的最长递增子序列(LIS),到底该怎么想、怎么写?
前端·javascript·算法
Zyx20073 小时前
手写 `instanceof`:深入理解 JavaScript 原型链与继承机制
javascript
江城开朗的豌豆3 小时前
TypeScript和JavaScript到底有什么区别?
前端·javascript
前端不太难4 小时前
如何给 RN 项目设计「不会失控」的导航分层模型
前端·javascript·架构
用户4099322502124 小时前
Vue3中v-show如何通过CSS修改display属性控制条件显示?与v-if的应用场景该如何区分?
前端·javascript·vue.js
Zyx20074 小时前
JavaScript 中 this 的设计哲学与运行机制
javascript
A24207349304 小时前
JavaScript图表制作:从入门到精通
开发语言·javascript·信息可视化