Electron实战之进程间通信

进程间通信(IPC)并非仅限于 Electron,而是源自甚至早于 Unix 诞生的概念。尽管“进程间通信”这个术语的确创造于何时并不清楚,但将数据传递给另一个程序或进程的理念可以追溯至 1964 年,当时 Douglas McIlroy 在 Unix 的第三版(1973 年)中描述了 Unix 管道的概念。

We should have some ways of coupling programs like garden hose--screw in another segment when it becomes when it becomes necessary to massage data in another way.

例如,我们可以通过使用管道操作符(|)将一个程序的输出传递到另一个程序。

# 列出当前目录下的所有.ts文件
ls | grep .ts

在 Unix 系统中,管道只是 IPC 的一种形式,还有许多其他形式,比如信号、消息队列、信号量和共享内存。

一、ipcMain 和 ipcRenderer

与 Chromium 相同,Electron 使用进程间通信(IPC)来在进程之间进行通信,在介绍 Electron 进程间通信前,我们必须先认识一下 Electron 的 2 个模块。

  • ipcMain 是一个仅在主进程中以异步方式工作的模块,用于与渲染进程交换消息。
  • ipcRenderer 是一个仅在渲染进程中以异步方式工作的模块,用于与主进程交换消息。

ipcMain 和 ipcRenderer 是 Electron 中负责通信的两个主要模块。它们继承自 NodeJS 的 EventEmitter 模块。在 EventEmitter 中允许我们向指定 channel 发送消息。channel 是一个字符串,在 Electron 中 ipcMain 和 ipcRenderer 使用它来发出和接收事件/数据。

// 接受消息
// EventEmitter: ipcMain / ipcRenderer
EventEmitter.on("string", function callback(event, messsage) {});// 发送消息
// EventEmitter: win.webContents / ipcRenderer
EventEmitter.send("string", "mydata");

二、渲染进程 -> 主进程

大多数情况下的通信都是从渲染进程到主进程,渲染进程依赖 ipcRenderer 模块给主进程发送消息,官方提供了三个方法:

  • ipcRenderer.send(channel, …args)
  • ipcRenderer.invoke(channel, …args)
  • ipcRenderer.sendSync(channel, …args)

channel 表示的就是事件名(消息名称), args 是参数。需要注意的是参数将使用结构化克隆算法进行序列化,就像浏览器的 window.postMessage 一样,因此不会包含原型链。发送函数、Promise、Symbol、WeakMap 或 WeakSet 将会抛出异常。

2.1 ipcRenderer.send

渲染进程通过 ipcRenderer.send 发送消息:

// render.js
import { ipcRenderer } from 'electron';function sendMessageToMain() {ipcRenderer.send('my_channel', 'my_data');
}

主进程通过 ipcMain.on 来接收消息:

// main.js
import { ipcMain } from 'electron';ipcMain.on('my_channel', (event, message) => {console.log(`receive message from render: ${message}`) 
})

请注意,如果使用 send 来发送数据,如果你的主进程需要回复消息,那么需要使用 event.replay 来进行回复:

// main.js
import { ipcMain } from 'electron';ipcMain.on('my_channel', (event, message) => {console.log(`receive message from render: ${message}`)event.reply('reply', 'main_data')
})

同时,渲染进程需要进行额外的监听:

// renderer.js
ipcRenderer.on('reply', (event, message) => { console.log('replyMessage', message);
})

2.2  ipcRenderer.invoke

渲染进程通过 ipcRenderer.invoke 发送消息:

// render.js
import { ipcRenderer } from 'electron';async function invokeMessageToMain() {const replyMessage = await ipcRenderer.invoke('my_channel', 'my_data');console.log('replyMessage', replyMessage);
}

主进程通过 ipcMain.handle 来接收消息:

// main.js
import { ipcMain } from 'electron';
ipcMain.handle('my_channel', async (event, message) => {console.log(`receive message from render: ${message}`);return 'replay';
});

注意,渲染进程通过 ipcRenderer.invoke 发送消息后,invoke 的返回值是一个 Promise 。主进程回复消息需要通过 return 的方式进行回复,而 ipcRenderer 只需要等到 Promise resolve 即可获取到返回的值。

2.3 ipcRender.sendSync

渲染进程通过 ipcRender.sendSync 来发送消息:

// render.js
import { ipcRenderer } from 'electron';async function sendSyncMessageToMain() {const replyMessage = await ipcRenderer.sendSync('my_channel', 'my_data');console.log('replyMessage', replyMessage);
}

主进程通过 ipcMain.on 来接收消息:

// main.js
import { ipcMain } from 'electron';
ipcMain.on('my_channel', async (event, message) => {console.log(`receive message from render: ${message}`);event.returnValue = 'replay';
});

注意,渲染进程通过 ipcRenderer.sendSync 发送消息后,主进程回复消息需要通过 e.returnValue 的方式进行回复,如果 event.returnValue 不为 undefined 的话,渲染进程会等待 sendSync 的返回值才执行后面的代码。

2.4 小结

上面我们介绍了从渲染进程到主进程的几个通信方法,总结如下。

  • ipcRenderer.send: 这个方法是异步的,用于从渲染进程向主进程发送消息。它发送消息后不会等待主进程的响应,而是立即返回,适合在不需要等待主进程响应的情况下发送消息。
  • ipcRenderer.sendSync: 与 ipcRenderer.send 不同,这个方法是同步的,也是用于从渲染进程向主进程发送消息,但是它会等待主进程返回响应。它会阻塞当前进程,直到收到主进程的返回值或者超时。
  • ipcRenderer.invoke: 这个方法也是用于从渲染进程向主进程发送消息,但是它是一个异步的方法,可以方便地在渲染进程中等待主进程返回 Promise 结果。相对于 send 和 sendSync,它更适合处理异步操作,例如主进程返回 Promise 的情况。

三、主进程 -> 渲染进程

主进程向渲染进程发送消息一种方式是当渲染进程通过 ipcRenderer.send、ipcRenderer.sendSync、ipcRenderer.invoke 向主进程发送消息时,主进程通过 event.replay、event.returnValue、return … 的方式进行发送。这种方式是被动的,需要等待渲染进程先建立消息推送机制,主进程才能进行回复。

其实除了上面说的几种被动接收消息的模式进行推送外,还可以通过 webContents 模块进行消息通信。

3.1 ipcMain 和 webContents

主进程使用 ipcMain 模块来监听来自渲染进程的事件,通过 event.sender.send() 方法向渲染进程发送消息。

// 主进程
import { ipcMain, BrowserWindow } from 'electron';ipcMain.on('messageFromMain', (event, arg) => {event.sender.send('messageToRenderer', 'Hello from Main!');
});

3.2 BrowserWindow.webContents.send

BrowserWindow.webContents.send 可以在主进程中直接使用 BrowserWindow 对象的 webContents.send() 方法向渲染进程发送消息。

// 主进程
import { BrowserWindow } from 'electron';const mainWindow = new BrowserWindow();
mainWindow.loadFile('index.html');// 在某个事件或条件下发送消息
mainWindow.webContents.send('messageToRenderer', 'Hello from Main!');

3.3 小结

不管是通过 event.sender.send() 还是 BrowserWindow.webContents.send 的方式,如果你只是单窗口的数据通信,那么本质上是没什么差异的。但是如果你想要发送一些数据到特定的窗口,那么你可以直接使用 BrowserWindow.webContents.send 这种方式。

四、渲染进程 -> 渲染进程

默认情况下,渲染进程和渲染进程之间是无法直接进行通信的。

image.png

虽然说无法直接通信,但是还是有一些“曲线救国”的方式。

4.1 利用主进程作为中间人

首先,需要在主进程注册一个事件监听程序,监听来自渲染进程的事件:

// main.js// window 1
function createWindow1 () {window1 = new BrowserWindow({width: 800,height: 600})window1.loadURL('window1.html')window1.on('closed', function () {window1 = null})return window1
}// window 2
function createWindow2 () {window2 = new BrowserWindow({width: 800, height: 600})window2.loadURL('window2.html')window2.on('closed', function () {window2 = null})return window2
}app.on('ready', () => {createWindow1();createWindow2();ipcMain.on('win1-msg', (event, arg) => {// 这条消息来自 window 1console.log("name inside main process is: ", arg); // 发送给 window 2 的消息.window2.webContents.send( 'forWin2', arg );});
})

然后,在 window2 窗口建立一个监听事件:

ipcRenderer.on('forWin2', function (event, arg){console.log(arg);
});

这样,window1 发送的 win1-msg 事件,就可以传输到 window2:

ipcRenderer.send('win1-msg', 'msg from win1');

4.2 使用 MessagePort

上面的传输方式虽然可以实现渲染进程之间的通信,但是非常依赖主进程,写起来也比较麻烦,那有什么不依赖于主进程的方式嘛?那当然也是有的,那就是 MessagePort。

MessagePort 并不是 Electron 提供的能力,而是基于 MDN 的 Web 标准 API,这意味着它可以在渲染进程直接创建。同时 Electron 提供了 node.js 侧的实现,所以它也能在主进程创建。

接下来,我们将通过一个示例来描述如何通过 MessagePort 来实现渲染进程之间的通信。

4.2.1 主进程中创建 MessagePort

import { BrowserWindow, app, MessageChannelMain } from 'electron';app.whenReady().then(async () => {// 创建窗口const mainWindow = new BrowserWindow({show: false,webPreferences: {contextIsolation: false,preload: 'preloadMain.js'}})const secondaryWindow = new BrowserWindow({show: false,webPreferences: {contextIsolation: false,preload: 'preloadSecondary.js'}})// 建立通道const { port1, port2 } = new MessageChannelMain()// webContents准备就绪后,使用postMessage向每个webContents发送一个端口。mainWindow.once('ready-to-show', () => {mainWindow.webContents.postMessage('port', null, [port1])})secondaryWindow.once('ready-to-show', () => {secondaryWindow.webContents.postMessage('port', null, [port2])})
})

实例化 MessageChannel 类之后,就产生了两个 port: port1 和 port2。接下来只要让 渲染进程1 拿到 port1、渲染进程2 拿到 port2,那么现在这两个进程就可以通过 port.onmessage 和 port.postMessage 来收发彼此间的消息了。如下:

// mainWindow
port1.onmessage = (event) => {console.log('received result:', event.data)
};
port1.postMessage('我是渲染进程一发送的消息');// secondaryWindow
port2.onmessage = (event) => {console.log('received result:', event.data)
};
port2.postMessage('我是渲染进程二发送的消息');

4.2.2 渲染进程中获取 port

有了上面的知识,我们最重要的任务就是需要获取主进程中创建的 port 对象,要做的是在你的预加载脚本(preload.js)中通过 IPC 接收 port,并设置相应的监听器。

// preloadMain.js
// preloadSecondary.js
const { ipcRenderer } = require('electron')ipcRenderer.on('port', e => {// 接收到端口,使其全局可用。window.electronMessagePort = e.ports[0]window.electronMessagePort.onmessage = messageEvent => {// 处理消息}
})

4.3 消息通信

通过上面的一些操作后,就可以在应用程序的任何地方调用 postMessage 方法向另一个渲染进程发送消息。

// mainWindow renderer.js
// 在 renderer 的任何地方都可以调用 postMessage 向另一个进程发送消息
window.electronMessagePort.postMessage('ping')

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/685878.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Windows 连接共享文件夹 切换/退出账号操作

工作中遇到个问题,登录公司内部共享文件夹,使用自己的账号,但需要切换别人账号找东西时,没有发现登出的地方。在网上找了两种方法:通过命令行登出账号的方法 (1)打开cmd命令提示符,…

对待不合理需求,前端工程师如何优雅的say no!

曾经有位老板, 每次给前端提需求,前端都说实现不了,后来他搜索了一下,发现网上都有答案。他就在招聘要求上加了条:麻烦你在说不行的时候,搜索一下。 上面是一个段子,说的有点极端了,…

Java集合篇之深入解析ArrayList,这六问你答的上来吗?

写在开头 开年第一篇,先祝各位新的一年身体健康,学业有成,事业有成哈,春节期间就是咔咔乱吃,咔咔乱玩,把学习都抛一边子去了,已经9天没有学习了,深深的懊悔,从今天开始&…

Leetcode1423.可获得的最大点数

文章目录 题目原题链接思路(逆向思维) 题目 原题链接 Leetcode1423.可获得的最大点数 思路(逆向思维) 由题目可知,从两侧选k张,总数为n张,即从中间选n - k张 nums总和固定,要选k张最…

[CTF]-PWN:C++文件更换libc方法(WSL)

C文件与C文件更换libc有很多不一样的地方,我是在写buu的ciscn_2019_final_3才意识到这个问题,C文件只需要更换libc和ld就可以了,但是C文件不同,除了更换libc和ld,它还需要更换libstdc.so.6和libgcc_s.so.1 更换libc和…

[SWPUCTF 2021 新生赛]crypto8

第一眼看见是乱码不确定是什么的编码 看了下感觉是UUencode编码 UUencode编码是一种古老的编码方式,通常用于将二进制数据转换成可打印字符的形式。UUencode编码采用一种基于64个字符的编码表,将每3个字节的数据编码为4个可打印字符,以实现…

vue导出word文档(图文示例)

第076个 查看专栏目录: VUE 本文章目录 示例说明示例效果图导出的文件效果截图示例源代码参数说明:重要提示:API 参考网址 示例说明 在Vue中导出Word文档,可以使用第三方库file-saver和html-docx-js。首先需要安装这两个库: npm …

库的操作【数据库】

目录 一、创建数据库 二、删除数据库 ​编辑 三、数据库编码问题 四、库的改查 查 1)查有哪些数据库: 2)使用某个数据库: 3)当前在哪个数据库: 4)有谁在使用 改alter 五、备份和恢复 …

JDK8新增的时间

设计更合理,功能更丰富,使用更方便,都是不可变的对象,修改后会返回新的事件对象不会丢失最开始的时间,线程安全,能精确到毫秒、纳秒。 这三个类都有一个静态方法now():获取系统当前时间对应的该…

Linux常用命令总结-2

文章目录 1. 关闭防火墙2. 用户组管理3. 系统的性能监控 1. 关闭防火墙 service iptables stop2. 用户组管理 1.添加用户 useradd 用户名需要在root用户下,否则没有权限 2. 删除用户 userdel 用户名3. 修改用户密码 passwd 用户名3. 系统的性能监控 1.监控cp…

Day01 javaweb开发——tlias员工管理系统

任务介绍 完成部门管理和员工管理的增删改查功能 环境搭建 前端---->后端---->数据库 准备数据库表创建springboot工程(web、mybatis、mysql驱动、lombok)application.properties中引入mybatis配置信息,准备对应的实体类准备三层架…

计算机组成原理:存储系统【四】

🌈个人主页:godspeed_lucip 🔥 系列专栏:计算机组成与原理基础 1 外存储器1.1 总览1.2 磁盘存储器1.2.1 磁盘设备的组成1.2.2 磁盘的性能指标1.2.3 磁盘的地址1.2.3 磁盘的工作过程 1.3 磁盘阵列1.3.1 解释1.3.2 分类 1.4 总结 2 …

map的key重复问题

一种需要key重复的Map 实例结果 IdentityHashMap<>(); dentityHashMap 类&#xff08;存在于java.util包中&#xff09;是一个 基于HashTable的 Map 接口的实现&#xff0c;从Java 1.4版本开始就已经存在。 这个类不是一个通用的Map 实现。尽管这个类实现了Map 接口&…

用于图像处理的Python顶级库 !!

文章目录 前言 1、OpenCV 2、Scikit-Image 3、Scipy 4、Python Image Library&#xff08;Pillow / PIL&#xff09; 5、Matplotlib 6、SimpleITK 7、Numpy 8、Mahotas 前言 正如IDC所指出的&#xff0c;数字信息将飙升至175ZB&#xff0c;而这些信息中的巨大一部分是图片。数…

DTAN: Diffusion-based Text Attention Network for medical imagesegmentation

DTAN:基于扩散的医学图像分割文本关注网络 摘要 在当今时代&#xff0c;扩散模型已经成为医学图像分割领域的一股开创性力量。在此背景下&#xff0c;我们引入了弥散文本注意网络(Diffusion text - attention Network, DTAN)&#xff0c;这是一个开创性的分割框架&#xff0c…

http“超级应用与理解”

本篇文章来介绍一下http协议和其应用 1.http协议是在OSI模型的哪一层 HTTP&#xff08;超文本传输协议&#xff09;是应用层协议&#xff0c;它是在 OSI 模型的最高层&#xff0c;即第七层——应用层。HTTP 通过互联网来传输数据和信息&#xff0c;主要用于 Web 浏览器和 Web …

前端开发,Vue的双向数据绑定的原理

目录 一、什么是前端 二、Vue.JS框架 三、双向数据绑定 四、Vue的双向数据绑定的原理 一、什么是前端 前端通常指的是网页或应用程序中用户直接交互和感知的部分&#xff0c;也称为客户端。前端开发涉及使用HTML、CSS和JavaScript等技术来构建用户界面和交互功能。前端开发…

SW2000TSN-千兆百兆车载以太网TSN交换机

更多资讯可以进入官网查看或者联系我们http://www.hdn-vdo.com

【分享】JLINK的SW调试模式连线方式

大家知道&#xff0c;JLINK有2种调试模式&#xff1a;JTAG和SWD&#xff08;串行模式&#xff09;。 JTAG是常用模式&#xff0c;大家都熟悉、不废话了&#xff1b;如果使用SW模式&#xff0c;需要&#xff08;只需要&#xff09;4根连线&#xff0c;连接方式如下&#xff1a; …

360安全浏览器_360se15.1.1453.64_优化版_【屏蔽文件关联】

360安全浏览器15正式版(360SE15)是基于Chromium内核的双核浏览器,Chromium内核提升至114,支持Win7系统,新增夜间模式,自动模式动态切换支持IE内核.各类实用功能,安全保护技术,丰富皮肤库,风格多样化. 360安全浏览器_360se6.64位_优化版 360安全浏览器PC官方版下载丨最新版下载…