主要参照electron中文官网,加上自己的一些理解,写的一个专题文章,如果觉得可以请点个赞或者关注一波,微信公众号:[猫十二的日常],欢迎留言和指出问题a
electron 通讯的方式
- 利用 `ipcMain` 和 `ipcRenderer` 模块
- 利用 electron.remote 模块
我们现在主要讲第一种通讯方式
理解主进程和渲染进程
主进程(Main Process)
一个electron只能有一个主进程
主进程指的是,你在执行 electron . 命令后,对应的当前目录下面的package.json文件下面的main的指定文件将会被执行,这里指的是
与创建GUI(类似创建窗口)相关的接口只能通过主进程来调用。
渲染进程(Renderer Process)
由主进程调用GUI接口创建的页面,都有自己的进程,叫做渲染进程。主进程通过实例化BrowserWindow,每个BrowserWindow实例都会渲染一个web页面,相互独立,当一个BrowserWindow被销毁,相应的渲染进程也会被终止。
渲染进程被主进程管理,每个渲染进程相互独立,只管理他们自己的web页面
两者需要通信,就需要用ipc通信
ipc通信的
在electron中我们需要主线程(Main Process)和渲染进程(Renderer Process)进行通信,需要用到两个模块
ipcMain
从主进程到渲染进程的异步通信。
ipcMain
是一个 EventEmitter 的实例。当在主进程中使用时,它处理从渲染器进程(网页)发送出来的异步和同步信息。从渲染器进程发送的消息将被发送到该模块。ipcRenderer
从渲染器进程到主进程的异步通信。
ipcRenderer
是一个 EventEmitter 的实例。你可以使用它提供的一些方法从渲染进程 (web 页面) 发送同步或异步的消息到主进程。也可以接收主进程回复的消息。
ipc通讯的相关方法
渲染进程和主进程通讯的方法
目录准备,修改和删除都在这几个文件操作
my-electron-app/
├── package.json
├── main.js // 这个就是主进程
├── renderer.js // 这个就是渲染进程
└── index.html //web页面
index.html
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>2020-01-08title>head><body>
Electron通讯演示<script src="./renderer.js">script> //外部引入jsbody>html>
main.js
const { app, BrowserWindow, ipcMain } = require("electron");
function createWindow() {
const win = new BrowserWindow({
width: 1920,
height: 1080,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
},
});
win.webContents.openDevTools(); //打开调试工具
win.loadFile("index.html");
}
app.whenReady().then(createWindow);
app.on("window-all-closed", () => {
if (process.platform !== "darwin") {
app.quit();
}
});
app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
ipcMain.on("async-message", (event, arg) => {
//异步消息回复
console.log(`async-message:我接收到了异步消息`, arg);
event.reply("async-reply", "哥们我收到消息了-来自异步");
});
ipcMain.on("sync-message", (event, arg) => {
//同步消息回复
console.log(`async-message:我接收到了同步消息`, arg);
event.returnValue = "哥们我收到消息了-来自同步";
});
renderer.js
const { ipcRenderer } = require("electron");
// 同步消息
let message = ipcRenderer.sendSync("sync-message", "发个同步消息");
console.log("同步消息:", message);
//异步消息
ipcRenderer.on("async-reply", (event, arg) => {
console.log("异步消息:", arg);
});
ipcRenderer.send("async-message", "发个异步消息");
package.json
{
"name": "2020-01-08",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"start": "electron ."
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"electron": "^11.1.1"
}
}
结果
electron(主进程)
async-message:我接收到了同步消息 发个同步消息
async-message:我接收到了异步消息 发个异步消息
客户端(渲染进程)
同步消息: 哥们我收到消息了-来自同步
异步消息: 哥们我收到消息了-来自异步
本节会基于这几个文件进行修改和操作
对于以上的内容主要用到了几个方法实现了渲染进程向主进程通信
ipcMain.on(channel, listener) 主进程监听来自渲染进程的通信,ipcMain
是一个 EventEmitter 的实例
channel 监听的事件名称,String类型
listener 监听的回调函数 有两个入参
event
IpcMainEvent ipc事件event
需要回复消息时有两种情况,一种是异步回复,一种是同步的回复回复同步信息时,需要设置
event.returnValue
。event.reply(...)
将异步消息发送回发送者两者都在上面的demo体现
...args
any[] 后面的都是客户端传过来的入参
IpcMainEvent 继承之Event有下列属性值
processId(Integer类型) 发送此消息的渲染器进程的内部ID
frameId Integer类型)发送该消息的渲染进程框架的ID(可能是iframe)
returnValue (any类型)) 将其设置为要在同步消息中返回的值
reply (Function类型) 将 IPC 消息发送到渲染器框架的函数,该渲染器框架发送当前正在处理的原始消息。您应该使用“reply”方法回复发送的消息,以确保回复将转到正确的进程和框架。
channel
String...args
any[]
**ipcRenderer.sendSync(channel, …args) ** 渲染进程发送同步消息给主进程
channel 事件名称,String类型
…args any[]
返回 any - 由 ipcMain 处理程序发送过来的值。
发送同步消息将阻止渲染器的渲染过程,类似于async await 模式,最好还是选择异步版本的处理方式
ipcRenderer.send(channel, …args),渲染进程向主进程发送异步消息
channel 事件名称,String类型
…args any[]
以上的所有args参数,只能是被序列化的字符串,所有的函数、promise、Symbol、weakMaps、weakSets都会抛出异常,DOM结点在electron9也会抛出异常
其他的函数
只监听一次事件
ipcMain.once(channel, listener)
channel 监听的事件名称,String类型
listener 监听的回调函数 有两个入参
event
IpcMainEvent ipc事件event
需要回复消息时有两种情况,一种是异步回复,一种是同步的回复回复同步信息时,需要设置
event.returnValue
。event.reply(...)
将异步消息发送回发送者两者都在上面的demo体现
...args
any[] 后面的都是客户端传过来的入参
该事件函数只会监听一次,监听完将会被移除
用ipcMain.on监听
# main.js
// 正常的监听回复
ipcMain.on("on-message", (event, arg) => {
event.reply("on-reply-message", arg);
});
# renderer.js
for (let index = 0; index < 5; index++) {
ipcRenderer.send("on-message", `第${index}次`);
}
ipcRenderer.on("on-reply-message", (event, arg) => {
console.log(`回复:${arg}`);
});
//结果
回复:第0次
回复:第1次
回复:第2次
回复:第3次
用ipcMain.once监听
# main.js
ipcMain.once("one-message", (event, arg) => {
event.reply("one-reply-message", arg);
});
# renderer.js
for (let index = 0; index < 5; index++) {
ipcRenderer.send("one-message", `第${index}次`);
}
ipcRenderer.on("one-reply-message", (event, arg) => {
console.log(`回复:${arg}`);
});
//结果
回复:第0次
ipcRenderer.once(channel, listener)
方法跟on事件监听差不多
该方法也是只监听主进程发的一次消息后,自动移除事件
# main.js
ipcMain.on("one-message", (event, arg) => {
event.reply("one-reply-message", arg);
});
# renderer.js
for (let index = 0; index < 5; index++) {
ipcRenderer.send("one-message", `第${index}次`);
}
ipcRenderer.once("one-reply-message", (event, arg) => {
console.log(`回复:${arg}`);
});
//结果
回复:第0次
移除监听事件
ipcMain.removeListener(channel, listener) 参数跟定义时的监听函数一致才能被移除
# main.js
function callback(event, arg) {
event.reply("reply", `主进程返回信息:${arg}`);
}
ipcMain.on("message", callback);
ipcMain.removeListener("message", callback);
# renderer.js
function watcher(event, arg) {
console.log(arg);
}
ipcRenderer.send("message", "渲染进程发信息");
ipcRenderer.on("reply", watcher);
//结果
空
ipcMain.removeAllListeners([channel])
channel可以移除这个事件的所有监听者
# main.js
function callback(event, arg) {
event.reply("reply", `主进程返回信息:${arg}`);
}
ipcMain.on("message", callback);
ipcMain.removeAllListeners("message");//指定的事件
//ipcMain.removeAllListeners(); //所有事件移除
# renderer.js
function watcher(event, arg) {
console.log(arg);
}
ipcRenderer.send("message", "渲染进程发信息");
ipcRenderer.on("reply", watcher);
//结果
空
异步的来回通信
这个主要的改变是类似返回promse来处理返回的结果,所以入参基本一致了
ipcMain.handle(channel, listener) 主进程的监听函数
ipcRenderer.invoke(channel, …args) 渲染进程的发送消息的函数
# main.js
ipcMain.handle("message", async (event, ...args) => {
let someMessage = await Promise.resolve("我是异步回来的消息");
return someMessage;
});
# renderer.js
(async () => {
let result = await ipcRenderer.invoke("message");
console.log(result);
})();
//结果
我是异步回来的消息
ipcMain.handleOnce(channel, listener) 单个执行一次
# main.js
ipcMain.handleOnce("message", async (event, ...args) => {
let someMessage = await Promise.resolve("我是异步回来的消息");
return someMessage;
});
# renderer.js
(async () => {
let result = await ipcRenderer.invoke("message");
console.log(result);
})();
//结果
我是异步回来的消息
ipcMain.removeHandler(channel)移除监听函数
# main.js
ipcMain.handle("message", async (event, ...args) => {
let someMessage = await Promise.resolve("我是异步回来的消息");
return someMessage;
});
ipcMain.removeHandler("message"); //一定要写移除那个事件名称
# renderer.js
(async () => {
let result = await ipcRenderer.invoke("message");
console.log(result);
})();
//结果
报错
使用webworker传递信息
**ipcRenderer.postMessage(channel, message, transfer])** 此函数类似于网页的中的 [webworker,百度没有查到其在electron的用法,自己按照webwork的方式写了一套 似乎是可以传递消息 (待更正)
# main.js
ipcMain.on("port", (e, msg) => {
const [port] = e.ports;
console.log(port, msg, e.ports);
port.postMessage("发信息给渲染进程");
});
# renderer.js
const { port1, port2 } = new MessageChannel();
ipcRenderer.postMessage("port", { message: "hello" }, [port2]);
port1.onmessage = (arg) => {
console.log(arg.data);
};
//结果
发信息给渲染进程
我们现在基本是讲了渲染进程主动向主进程发送消息并且得到回复,实际的情况,我们还有每个渲染进程互相通信,以及主进程主动向某个渲染进程通讯的操作 请听下回分解吧
如果觉得我的文章还可以,点个赞或者关注一下下,还有精彩的故事等着你哦,还可以云撸猫