网站开发技术有,wordpress商品,赣州网站建设价格,百度投诉中心24人工进程间通信#xff08;IPC#xff09;并非仅限于 Electron#xff0c;而是源自甚至早于 Unix 诞生的概念。尽管“进程间通信”这个术语的确创造于何时并不清楚#xff0c;但将数据传递给另一个程序或进程的理念可以追溯至 1964 年#xff0c;当时 Douglas McIlroy 在 Unix…进程间通信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 这种方式。
四、渲染进程 - 渲染进程
默认情况下渲染进程和渲染进程之间是无法直接进行通信的。 虽然说无法直接通信但是还是有一些“曲线救国”的方式。
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)