Electron中主进程和渲染进程间的通信

electron

0x00 写在前面

  在最近的项目中,用到了electron来进行桌面应用的开发。开发过程中涉及到从数据库中获取数据,并在页面中进行渲染这一过程。这一块也是electron开发中很重要的一个知识点:主进程和渲染进程间的通信。

0x01 主进程—ipcMain

  ipcMain模块是EventEmitter类的一个实例。 当在主进程中使用时,它处理从渲染器进程(网页)发送出来的异步和同步信息。 从渲染器进程发送的消息将被发送到该模块。

1. 发送消息
  • 发送消息时,事件名称为channel。
  • 回复同步信息时,需要设置event.returnValue。
  • 将异步消息发送回发件人,需要使用event.sender.send(…)。
2. 方法

  IpcMain模块可以使用以下方法来侦听事件:

  • ipcMain.on(channel[String], listener[Function])

  监听一个事件,当接收到新的消息时 listener 会以 listener(event, args…) 的形式被调用。

  • ipcMain.once(channel[String], listener[Function])

  这个方法只添加一次性的监听事件,当且仅当下一个消息发送到 channel 时 listener 才会被调用,随后 listener 会被移除。

  • ipcMain.removeListener(channel[String], listener[Function])

  从监听器数组中移除监听事件(channel)指定的监听器(listener)。

  • ipcMain.removeAllListeners([channel][String])

  删除所有监听者,或特指的 事件(channel) 的所有监听者.

3. 事件对象

  主进程向callback传递event对象可以有如下方法:

  • event.returnValue

  在一个同步消息中使用该方法返回值.

  • event.sender

  在一个异步消息中使用该方法返回值.

0x02 渲染进程—ipcRenderer

  ipcRenderer 是一个 EventEmitter 的实例。 可以使用它提供的一些方法从渲染进程 (web 页面) 发送同步或异步的消息到主进程。 也可以接收主进程回复的消息。

1. 方法

  ipcRenderer 模块可以使用以下方法来监听事件和发送消息。

  • ipcRenderer.on(channel[String], listener[Function])

  监听事件(channel), 当新消息到达,将通过 listener(event, args…) 调用监听器(listener)。

  • ipcRenderer.once(channel[String], listener[Function])

  为事件添加一个一次性用的监听器(listener) 函数.这个监听器 只有在下次的消息到达 事件(channel) 时被请求调用,之后就被删除了.

  • ipcRenderer.removeListener(channel[String], listener[Function])

  为特定的事件(channel)从监听队列中删除特定的 listener 监听者.

  • ipcRenderer.removeAllListeners(channel[String])

  移除所有的监听器,当指定 channel 时只移除与其相关的所有监听器。

  • ipcRenderer.send(channel[, arg1][, arg2][, …])

  通过事件(channel)发送异步消息到主进程,可以携带任意参数。 在内部,参数会被序列化为 JSON,因此参数对象上的函数和原型链不会被发送。主进程可以使用 ipcMain 监听事件(channel) .

  • ipcRenderer.sendSync(channel[, arg1][, arg2][, …])

  返回任何由ipcMain处理程序发送过来的值。通过 channel 发送同步消息到主进程,可以携带任意参数。 在内部,参数会被序列化为 JSON,因此参数对象上的函数和原型链不会被发送。主进程可以使用 ipcMain 监听 channel来接收这些消息,并通过 event.returnValue 设置回复消息。
需要注意的是: 发送同步消息将会阻塞整个渲染进程。

0x03 渲染进程—remote

  可以使用 remote 模块来调用 main 进程对象的方法,而不必显式发送进程间消息。也就是说,可以使用remote在渲染进程中使用主进程模块。
比如,可以利用下面的代码在渲染进程中新建一个浏览器窗口。

1
2
3
4
//渲染进程
const {BrowserWindow} = require('electron').remote
let win = new BrowserWindow({width: 800, height: 600})
win.loadURL('https://miss-me.github.io/')
1. 远程对象

  remote 模块返回的每个对象 (包括函数) 表示主进程中的一个对象 (称之为远程对象或远程函数)。 当调用远程对象的方法时, 调用远程函数, 或者使用远程构造函数 (函数) 创建新对象时, 实际上是在发送同步进程消息。
  在上面的渲染进程中新建浏览器窗口时,BrowserWindow和win都是远程对象,new BrowserWindow并不是在渲染进程中创建BrowserWindow对象,而是在主进程中创建了一个BrowserWindow对象,并在渲染进程中返回相应的远程对象,即win对象。
  【注意】Electron确保只要渲染进程中的远程对象存在(换句话说,没有被垃圾收集),主进程中的相应对象将不会被释放。也就是说,当远程对象被垃圾回收后,主进程中的相应对象将被接触引用。在这种情况下,如果远程对象在渲染进程中泄露(存储在映射中,但从未释放),则主进程中的相应对象也将被泄露。

2. 访问主进程中的内置模块

  主进程中的内置模块被添加为 remote 模块中的获取器,因此可以像 electron 模块一样直接使用它们。

1
2
const app = require('electron').remote.app
console.log(app)
3. 方法
  • remote.require(module[String])

  返回任何主进程中require(module) 返回的对象。 由其相对路径指定的模块将相对于主进程的入口点来解析。

1
2
3
// main process: main/index.js
const {app} = require('electron')
app.on('ready', () => { /* ... */ })
1
2
// some relative module: main/foo.js
module.exports = 'bar'
1
2
// renderer process: renderer/index.js
const foo = require('electron').remote.require('./foo') // bar
  • remote.getCurrentWindow()

  返回 BrowserWindow - 此网页所属的窗口

  • remote.getCurrentWebContents()

  返回 WebContents - 此网页的 web 内容

  • remote.getGlobal(name[String])

  返回任何主进程中 name (例如 global[name])的全局变量。

4. 属性
  • remote.process

  主进程中的 process 对象。这与 remote.getGlobal(‘process’) 相同, 但已被缓存。

0x04 进程间通信
1. 同步消息
  • 渲染进程
1
2
3
const ipcRenderer = require('electron').ipcRenderer//引入ipcRenderer
const reply = ipc.sendSync('synchronous-message', 'ping')//向'synchronous-message'事件发送一个异步消息:字段'ping'
  • 主进程
1
2
3
4
5
const ipcMain = require('electron').ipcMain
//主进程通过监听'synchronous-message'这个事件,接收渲染进程发送过来的值,也就是这里的arg
ipcMain.on('synchronous-message', function (event, arg) {
event.returnValue = 'pong'//向渲染进程返回一个值,同步返回消息
})
2. 异步消息
  • 渲染进程
1
2
3
4
5
6
7
8
const ipcipcRenderer = require('electron').ipcRenderer
ipc.send('asynchronous-message', 'ping')//渲染进程向主进程发送一个异步消息
ipcRenderer.on('asynchronous-reply', function (event, arg) {//渲染进程监听一个异步返回消息的事件,主进程返回的消息保存在arg里
const message = arg
})
  • 主进程
1
2
3
4
5
const ipcMain = require('electron').ipcMain
ipcMain.on('asynchronous-message', function (event, arg) {//主进程监听来自渲染进程的异步消息,并返回一个消息
event.sender.send('asynchronous-reply', 'pong')//异步返回数据
})
3. 使用remote通信
1
2
3
// 渲染进程
//渲染remote接口直接获取渲染进程发送消息。
remote.BrowserWindow.fromId(winId).webContents.send('ping', 'someThing');
1
2
3
4
5
6
7
8
// 主进程
//主进程创建事件,发送消息
win1.webContents.send('distributeIds',{
win2Id : win2.id
});
win2.webContents.send('distributeIds',{
win1Id : win1.id
});

https://segmentfault.com/a/1190000009253100

4. 渲染进程间的通信

  使用 ipcRenderer.sendTo() 在渲染进程中互相发送通信,更加容易。

1
ipcRenderer.sendTo(windowId, 'ping', 'someThing')

Miss Me wechat
light