简介
Electron是由GIthub开发,用HTML、CSS、JS来构建跨平台桌面应用程序的一个开源库。Electron将Chromium和Nodejs合并到同一个运行时环境中,并将其打包为Mac、Windows、Linux系统下的应用。可以将其看作是一个由JS控制的迷你版的Chromium浏览器。
主进程、渲染进程
Electron打包的应用包含两个部分:Electron的环境(node) — 主进程、web渲染环境 — 渲染进程。
Electron中,入口是js文件(通常为package.json里的main脚本),运行此文件的进程即为主进程,在主进程中使用BrowserWindow模块可以创建并管理web页面,也就是应用的GUI。
const {BrowserWindow} = require('electron');
// 创建浏览器窗口
let win = new BrowserWindow({width: 320, height: 572, resizable: true});
// 加载本地的文件
win.loadURL('file://' + __dirname + '/index.html');
// 打开调试窗口
win.webContents.openDevTools();
在主进程创建的每个web页面都运行着自己的进程,即渲染进程,渲染进程各自独立。
主进程和渲染进程之间是相互隔离的,无法直接进行数据通信。
进程间通信
Web页面因为安全限制,不能直接访问原生的GUI资源,Electron也一样,渲染进程如果想要进行原生的GUI操作,必须和主进程通信,请求相应的GUI操作。
Electron提供了集中渲染进程和主进程通信的方式:
- 使用ipcMain和ipcRenderer模块;
- 直接在渲染进程中使用remote模块;
- 主进程向渲染进程发送消息;
- 渲染进程之间的通信。
目前项目中用到的通信方式为第一种,此文只详细介绍第一种。
使用ipcMain和ipcRenderer模块
在渲染进程中使用ipcRenderer模块向主进程发送消息,主进程中ipcMain接收消息,进行操作,如需反馈,则通知渲染进程,渲染进程根据接收的内容执行相应的操作:
// 渲染进程
const ipcRenderer = require('electron').ipcRenderer;
// 同步消息使用sendSync方法,在发出瞬间即可接收到响应内容
console.log(ipcRenderer.sendSync('synchronous-message', 'ping')); // prints "pong"
ipcRenderer.on('asynchronous-reply', function(event, arg) {
console.log(arg); // prints "pong"
});
// 异步消息使用send方法,需要监听异步事件才能得到响应
ipcRenderer.send('asynchronous-message', 'ping');
// 主进程
const ipcMain = require('electron').ipcMain;
ipcMain.on('asynchronous-message', function(event, arg) {
console.log(arg); // prints "ping"
// 回应异步消息
event.sender.send('asynchronous-reply', 'pong');
});
ipcMain.on('synchronous-message', function(event, arg) {
console.log(arg); // prints "ping"
// 回应同步消息
event.returnValue = 'pong';
});
⚠️ 切忌用ipc传递大量的数据,会有很大的性能问题,严重会让整个应用卡住。
ipcMain模块 监听消息 相关函数
/**
* 监听 channel 事件,当新消息到达,通过listener(event, args...)调用listener
* @param {String} channel
* @param {Function} listener
*/
ipcMain.on(channel, listener);
/**
* 同上,但只触发一次,触发后自动取消绑定
* @param {String} channel
* @param {Function} listener
*/
ipcMain.once(channel, listener);
/**
* 为特定的 channel 从监听队列中删除特定的 listener 监听者
* @param {String} channel
* @param {Function} listener
*/
ipcMain.removeListener(channel, listener);
/**
* 删除所有监听者,或特指的 channel 的所有监听者
* @param {String} channel
* @param {Function} listener
*/
ipcMain.removeAllListeners([channel]);
ipcRenderer模块 监听消息 相关函数
/**
* 监听 channel 事件,当新消息到达,通过listener(event, args...)调用listener
* @param {String} channel
* @param {Function} listener
*/
ipcRenderer.on(channel, listener);
/**
* 同上,但只触发一次,触发后自动取消绑定
* @param {String} channel
* @param {Function} listener
*/
ipcRenderer.once(channel, listener);
/**
* 为特定的 channel 从监听队列中删除特定的 listener 监听者
* @param {String} channel
* @param {Function} listener
*/
ipcRenderer.removeListener(channel, listener);
/**
* 删除所有监听者,或特指的 channel 的所有监听者
* @param {String} channel
* @param {Function} listener
*/
ipcRenderer.removeAllListeners([channel]);
ipcRenderer模块 发送消息 相关函数
/**
* 通过 channel 向主进程发送异步消息,也可以发送任意参数.参数会被JSON序列化,之后就不会包含函数或原型链。主进程通过使用 ipcMain 模块来监听 channel,从而处理消息.通过 event.sender.send() 来响应.
* @param {String} channel
* @param {} [arg]
*/
ipcRenderer.send(channel[, arg1][, arg2][, ...]);
/**
* 通过 channel 向主进程发送同步消息,也可以发送任意参数.参数会被JSON序列化,之后就不会包含函数或原型链.主进程通过使用 ipcMain 模块来监听 channel,从而处理消息, 通过 event.returnValue 来响应.
* @param {String} channel
* @param {} [arg]
*/
// ⚠️发送同步消息将会阻塞整个渲染进程,除非你知道你在做什么,否则就永远不要用它 .
ipcRenderer.sendSync(channel[, arg1][, arg2][, ...]);
/**
* 类似 ipcRenderer.send ,但是它的事件将发往 host page 的 <webview> 元素,而不是主进程.
* @param {String} channel
* @param {} [arg]
*/
ipcRenderer.sendToHost(channel[, arg1][, arg2][, ...]);
直接在渲染进程中使用remote模块
remote 模块可以直接获取主进程中的模块。
// 在渲染进程打开提示对话框
const {dialog} = require('electron').remote
dialog.showMessageBox({ opts });
主进程向渲染进程发送消息
this.webviewWindow.webContents.send('ping');
渲染进程间通信
如果数据不需要实时性,只是渲染进程之间数据的共享,那么使用官方建议即可。如果要求实时性,需要配合前几种种方式实现。
// 主进程
// 两个窗口互相获取对方的窗口 id, 并发送给渲染进程
win1.webContents.send('distributeIds',{
win2Id : win2.id
});
win2.webContents.send('distributeIds',{
win1Id : win1.id
});
// 渲染进程
// 通过 id 得到窗口
remote.BrowserWindow.fromId(win2Id).webContents.send('someMsg', 'someThing');