electron中的ipc通信(二)

5,368 阅读6分钟

主要参照electron中文官网,加上自己的一些理解,写的一个专题文章,如果觉得可以请点个赞或者关注一波,微信公众号:[猫十二的日常],欢迎留言和指出问题a

electron 通讯的方式

- 利用 `ipcMain``ipcRenderer` 模块
- 利用 electron.remote 模块

我们现在主要讲第一种通讯方式

理解主进程和渲染进程

主进程(Main Process)

  • 一个electron只能有一个主进程
  • 主进程指的是,你在执行 electron . 命令后,对应的当前目录下面的package.json文件下面的main的指定文件将会被执行,这里指的是

image-20210108110957751

- 与创建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

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>2020-01-08</title>
</head>

<body>
    Electron通讯演示
    <script src="./renderer.js"></script> //外部引入js
</body>

</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);
};
//结果
发信息给渲染进程

我们现在基本是讲了渲染进程主动向主进程发送消息并且得到回复,实际的情况,我们还有每个渲染进程互相通信,以及主进程主动向某个渲染进程通讯的操作 请听下回分解吧

如果觉得我的文章还可以,点个赞或者关注一下下,还有精彩的故事等着你哦,还可以云撸猫