专业IM即时通讯软件开发,值得信赖!

对electron主进程和渲染进程的理解

未分类 云聊IM 35℃

最近在用electron做一个IM的PC端,因为之前只是对electron有些许了解,并不理解其中的一些概念,做起来还是费了不少时间。

一个electron应用包含一个主进程和多个渲染进程,主进程就是可以调用系统功能的那个进程,一个项目有且只有一个主进程;一个渲染进程则对应了一个网页,每创建一个web页面都会创建一个渲染进程,每个web页面运行在它自己的渲染进程中,每个渲染进程是独立的, 它只关心它所运行的页面。

开发时必须要区分一个模块是主进程的模块还是渲染进程的模块才能正常进行。因为主进程只能调用主进程模块,渲染进程只能调用渲染进程模块。

常见的主进程模块,app、BrowserWindow、globalShortcut、Tray、webContents。这些模块在渲染进程是无法调用的。

常见的渲染进程模块,ipcRenderer、remote等。如果使用vue框架,则vue实例、vuex、vuerouter这些也都是渲染进程模块。

因为electron原生的在内部分为主进程和渲染进程,两个进程之间的通信也是经常不可避免的。

在渲染进程访问主进程对象

let { remote } = require('electron');
remote.getCurrentWindow().webContents.openDevTools();
remote.getCurrentWebContents().openDevTools();

require是Node.js模块加载指令,需要在创建窗口的时候开启nodeIntegration。这里可以加载任何安装的Node.js模块。在此加载了Electron内部的remote模块。渲染进程可以通过这个模块访问主进程的模块、对象和方法。

在渲染进程访问主进程类型

除了常用的getCurrentWindow方法和getCurrentWebContents方法外,你还可以通过remote对象访问主进程的app、BrowserWindow等对象和类型

win = new remote.BrowserWindow({
        webPreferences: { nodeIntegration: true }
});
win.loadFile('index.html');

渲染进程访问主进程自定义内容

先在工程内新建一个mainModel.js文件,代码如下:

let { BrowserWindow } = require('electron')
exports.makeWin = function() {
    let win = new BrowserWindow({
        webPreferences: { nodeIntegration: true }
    });
    return win;
}

此模块导出了一个创建窗口的函数,再在index.html中增加如下代码:

let mainModel = remote.require('./mainModel');
let win2 = null;
document.querySelector("#makeNewWindow2").addEventListener('click', () => {
    win2 = mainModel.makeWin();
    win2.loadFile('index.html');
});

makeNewWindow2亦是页面中的一个按钮,点击此按钮,依然会打开一个新窗口。

主进程访问渲染进程对象

因为渲染进程是由主进程创建的,所以主进程可以很方便地访问渲染进程的对象与类型,比如创建一个窗口之后,马上控制这个窗口加载一个URL路径winObj.webContents.loadURL(‘…’)。
除此之外,主进程还可以访问渲染进程的刷新网页接口、打印网页内容接口等。

渲染进程向主进程发送消息

渲染进程使用Electron内置的ipcRenderer模块向主进程发送消息

let { ipcRenderer } = require('electron');
document.querySelector("#sendMsg1").addEventListener('click', () => {
    ipcRenderer.send('msg_render2main', { name: 'param1' }, { name: 'param2' });
});

ipcRenderer.send方法的第一个参数是消息管道的名称,主进程会根据该名称接收消息;后面两个对象是随消息传递的数据。该方法可以传递任意多个数据对象。

主进程通过ipcMain接收消息,代码如下:

let { ipcMain } = require('electron')
ipcMain.on('msg_render2main', (event, param1, param2) => {
    console.log(param1);
    console.log(param2);
    console.log(event.sender);
});

如果在主进程中设置了多处监听同一管道的代码,当该管道有消息发来时,则多处监听事件都会被触发。

如果需要主进程同步处理,那么可以通过ipcRenderer.sendSync方式发送消息

主进程向渲染进程发送消息

如果我们想在主进程中通过控制渲染进程的webContents来向渲染进程发送消息,需要在主进程index.js文件中增加如下代码:

ipcMain.on('msg_render2main', (event, param1, param2) => {
    win.webContents.send('msg_main2render', param1, param2)
});
ipcRenderer.on('msg_main2render', (event, param1, param2) => {
    console.log(param1);
    console.log(param2);
    console.log(event.sender);
});

改消息只会发给win渲染的页面

如果我们需要在多个窗口的渲染进程中给主进程发送同样的消息,消息发送完成之后,需要主进程响应消息给发送者,也就是发送消息给对应的渲染进程,该如何处理呢?可使用如下代码:

ipcMain.on('msg_render2main', (event, param1, param2) => {
    event.sender.send('msg_main2render', param1, param2)
});
ipcMain.on('msg_render2main', (event, param1, param2) => {
    event.reply('msg_main2render', param1, param2)
});

如果渲染进程传递的是同步消息,可以直接设置event的returnValue属性响应消息给渲染进程。需要注意的是,以这种方式返回数据给渲染进程,渲染进程是不需要监听的,当消息发送调用成功时,返回值即为主进程设置的event.returnValue的值,代码如下:

// returnValue为主进程的返回值
let returnValue = ipcRenderer.sendSync('msg_render2main', {  name: 'param1' }, 
{ name: 'param2' });

渲染进程之间消息传递

如果一个程序有多个窗口,并要在窗口之间传递消息,可以通过主进程中转,即窗口A先把消息发送给主进程,主进程再把这个消息发送给窗口B,这就完成了窗口A和窗口B的通信。因为窗口的创建工作往往是由主进程完成的,所以主进程持有所有窗口的实例,通过主进程中转窗口间消息非常常见。

但如果你在窗口A的渲染进程中知道窗口B的webContents的id,就可以直接从窗口A发送消息给窗口B,代码如下:

// win1窗口发送消息的代码
document.querySelector("#sendMsg2").addEventListener('click', _ => {
    ipcRenderer.sendTo(win2.webContents.id, 'msg_render2render', { name: 'param1' }, { name: 'param2'
    })
});
// win2窗口接收消息的代码
ipcRenderer.on('msg_render2render', (event, param1, param2) => {
    console.log(param1);
    console.log(param2);
    console.log(event.sender);
});

ipcRenderer.sendTo的第一个参数设置为目标窗口的webContents.id,第二个参数才是管道名称。

注意,在发送消息之前,你应该先打开win2所代表的窗口。接收消息的监听函数与接收主进程消息的监听函数是一样的。

文中大部分内容摘抄自《Electron实战:入门、进阶与性能优化》

喜欢 (3)
仿微信聊天软件开发
点击这里给我发消息