Electron系列文章-进程间通信

avatar
SugarTurboS Club @SugarTurboS

进程之间通信

在使用Electron开发应用的时候,我们往往需要在主进程和渲染进程之间互相传递数据来实现产品的需求。

假设我们现在要做一个APP,APP中有一个窗口来显示当前机器的CPU型号,总内存等信息。逻辑很简单,只需要拿到相关信息后,展示在窗口中就行了。在Electron的渲染进程中,没有直接提供API去获取相关的信息,所以要借助主进程去获取信息后,传递到渲染进程显示。

要实现这样的功能,我们可以用以下三种方式来实现

  • 方法调用
  • 数据共享
  • 数据消息

我们来用代码例子来分别讲解下这三种方式

示例程序的主要目录结构如下

demo/
├── main
│   ├── systemInfo.js
│   └── index.js
├── package.json
└── renderer
    └── index.js

方法调用

//主进程代码 main/systemInfo.js
var os = require('os');

//获取CPU信息
function getCpu(){
    var cores = os.cpus();
    if(cores.length > 0){
        return cores[0].model;
    }
}

exports.getCpu = getCpu;
//渲染进程代码 renderer/index.js
//我们需要在这里获取主进程才能获取的信息
const obj = require('electron').remote.require('./systemInfo')
const cpuInfo = obj.getCpu();
console.log(cpuInfo);

在这个例子中,我们通过Electron提供的remote模块去require一个主进程才能执行的模块,拿到需要的信息。这样我们就能在渲染进程的窗口中,显示设备信息。接下来再看看数据共享怎么实现。

数据共享

上面的例子使用了remote.require去间接调用主进程方法实现,在数据共享中,不直接调用方法,而是直接从主进程拿到相关数据。

//主进程代码 main/systemInfo.js
const os = require('os');

//获取CPU信息
function getCpu(){
    const cores = os.cpus();
    if(cores.length > 0){
        return cores[0].model;
    }
}

exports.getCpu = getCpu;
//主进程代码 main/index.js
const getCpu = require('./systemInfo');
const cpuInfo = getCpu();
global['cpuInfo'] = cpuInfo;
//渲染进程代码 renderer/index.js
const cpuInfo = require('electron').remote.getGlobal('cpuInfo');

在这个例子中,我们在主进程中先通过systemInfo模块获取信息,然后把信息存入Electron的全局对象global中。在渲染进程中,通过remote对象提供的getGlobal方法,可以直接获取到全局对象global中的属性值,实现我们想要的功能。这个方案的缺点是,渲染进行想要的信息,要在获取之前先生成好并存入global对象的属性中,每次获取的都是固定的值。而对于上个例子而言,较为被动,因为上个例子相当于是主动获取,当值改变时,可以拿到最新的值。

上面的两种实现方法都是通过remote模块实现的,那remote模块到底是什么呢?

remote模块是Electorn专门设计用来增强渲染进程能力的对象,他提供了诸如require、getGlobal等方法来获取主进程的一些信息。当通过remote去调用远程方法和共享变量时,实际上是在发送相应的同步消息。关于消息,我们先看下面的例子。

数据消息

同样,我们要在渲染进程获取数据

//主进程代码 main/systemInfo.js
const os = require('os');

//获取CPU信息
function getCpu(){
    const cores = os.cpus();
    if(cores.length > 0){
        return cores[0].model;
    }
}

exports.getCpu = getCpu;
//主进程代码 main/index.js
const getCpu = require('./systemInfo');
const ipc = require('electron').ipcMain;

ipc.on('get-cpu-info', function (event, arg) {
  event.sender.send('cpu-info-reply', getCpu())
})
//渲染进程代码 renderer/index.js
const ipc = require('electron').ipcRenderer;
const getCpuInfoBtn = document.getElementById('info-btn');
getCpuInfoBtn.addEventListener('click', function () {
  ipc.send('get-cpu-info');
})

ipc.on('cpu-info-reply', function (event, arg) {
    console.log(arg);
})

这里我们没有使用remote模块,而是使用了Electron提供的IPC模块来实现需求。IPC模块是Electron提供的用于主进程与渲染进程通信的模块,使用上跟事件的发布订阅类似,在消息的发起方通过ipc模块send事件,同时在接收方监听对应事件。在第二个例子中的结尾,说到的remote的调用实际上是在发送同步消息,也是利用了ipc的sync消息同样的原理来实现的。

在这个需求中,是渲染进程需要向主进程拿信息。有些场景下,可能是主进程要主动向渲染进程拿消息,实现方式类似,只需要更换下主次。

总结

主进程与渲染进程间通信所涉及的两大模块

  • remote:能通过方法直接调用以及全局变量获取来实现数据的传递
  • ipc:能通过事件注册发布的方式实现数据传递