渲染进程和主进程分别可调用的 Electron API。所有Electron的API都被指派给一种进程类型。 许多 API 只能被用于主进程中,有些API又只能被用于渲染进程,又有一些主进程和渲染进程中都可以使用。
你可以通过如下方式获取Electron API
const { BrowserWindow, ... } = require('electron')
下面是一些常用的Electron API:
在后面会选择其中常用的模块进行详细介绍。
可以同时在Electron的主进程和渲染进程使用Node.js API,)所有在Node.js可以使用的API,在Electron中同样可以使用。
import {shell} from 'electron';
import os from 'os';
document.getElementById('btn').addEventListener('click', () => {
shell.showItemInFolder(os.homedir());
})
有一个非常重要的提示: 原生Node.js模块 (即指,需要编译源码过后才能被使用的模块) 需要在编译后才能和Electron一起使用。
主进程和渲染进程虽然拥有不同的职责,然是他们也需要相互协作,互相通讯。
例如:
在web页面管理原生GUI资源是很危险的,会很容易泄露资源。所以在web页面,不允许直接调用原生GUI相关的API。渲染进程如果想要进行原生的GUI操作,就必须和主进程通讯,请求主进程来完成这些操作。
ipcRenderer 是一个 EventEmitter 的实例。 你可以使用它提供的一些方法,从渲染进程发送同步或异步的消息到主进程。 也可以接收主进程回复的消息。
在渲染进程引入ipcRenderer:
import { ipcRenderer } from 'electron';
异步发送:
通过 channel 发送同步消息到主进程,可以携带任意参数。
在内部,参数会被序列化为 JSON,因此参数对象上的函数和原型链不会被发送。
ipcRenderer.send('async-render', '我是来自渲染进程的异步消息');
同步发送:
const msg = ipcRenderer.sendSync('sync-render', '我是来自渲染进程的同步消息');
注意: 发送同步消息将会阻塞整个渲染进程,直到收到主进程的响应。
主进程监听消息:
ipcMain模块是EventEmitter类的一个实例。 当在主进程中使用时,它处理从渲染器进程(网页)发送出来的异步和同步信息。 从渲染器进程发送的消息将被发送到该模块。
ipcMain.on:监听 channel,当接收到新的消息时 listener 会以 listener(event, args…) 的形式被调用。
ipcMain.on('sync-render', (event, data) => {
console.log(data);
});
在主进程中可以通过BrowserWindow的webContents向渲染进程发送消息,所以,在发送消息前你必须先找到对应渲染进程的BrowserWindow对象。:
const mainWindow = BrowserWindow.fromId(global.mainId);
mainWindow.webContents.send('main-msg', `ConardLi]`)
根据消息来源发送:
在ipcMain接受消息的回调函数中,通过第一个参数event的属性sender可以拿到消息来源渲染进程的webContents对象,我们可以直接用此对象回应消息。
ipcMain.on('sync-render', (event, data) => {
console.log(data);
event.sender.send('main-msg', '主进程收到了渲染进程的【异步】消息!')
});
渲染进程监听:
ipcRenderer.on:监听 channel, 当新消息到达,将通过listener(event, args…)调用 listener。
ipcRenderer.on('main-msg', (event, msg) => {
console.log(msg);
})
ipcMain 和 ipcRenderer 都是 EventEmitter 类的一个实例。EventEmitter 类是 NodeJS 事件的基础,它由 NodeJS 中的 events 模块导出。
EventEmitter 的核心就是事件触发与事件监听器功能的封装。它实现了事件模型需要的接口, 包括 addListener,removeListener, emit 及其它工具方法. 同原生 JavaScript 事件类似, 采用了发布/订阅(观察者)的方式, 使用内部 _events 列表来记录注册的事件处理器。
我们通过 ipcMain和ipcRenderer 的 on、send 进行监听和发送消息都是 EventEmitter 定义的相关接口。
remote 模块为渲染进程(web页面)和主进程通信(IPC)提供了一种简单方法。 使用 remote 模块, 你可以调用 main 进程对象的方法, 而不必显式发送进程间消息, 类似于 Java 的 RMI 。
import { remote } from 'electron';
remote.dialog.showErrorBox('主进程才有的dialog模块', '我是使用remote调用的')
但实际上,我们在调用远程对象的方法、函数或者通过远程构造函数创建一个新的对象,实际上都是在发送一个同步的进程间消息。
在上面通过 remote 模块调用 dialog 的例子里。我们在渲染进程中创建的 dialog 对象其实并不在我们的渲染进程中,它只是让主进程创建了一个 dialog 对象,并返回了这个相对应的远程对象给了渲染进程。
Electron并没有提供渲染进程之间相互通信的方式,我们可以在主进程中建立一个消息中转站。
渲染进程之间通信首先发送消息到主进程,主进程的中转站接收到消息后根据条件进行分发。
在两个渲染进程间共享数据最简单的方法是使用浏览器中已经实现的HTML5 API。 其中比较好的方案是用Storage API, localStorage,sessionStorage 或者 IndexedDB。
就像在浏览器中使用一样,这种存储相当于在应用程序中永久存储了一部分数据。有时你并不需要这样的存储,只需要在当前应用程序的生命周期内进行一些数据的共享。这时你可以用 Electron 内的 IPC 机制实现。
将数据存在主进程的某个全局变量中,然后在多个渲染进程中使用 remote 模块来访问它。
global.mainId = ...;
global.device = {...};
global.__dirname = __dirname;
global.myField = { name: 'ConardLi' };
在渲染进程中读取:
import { ipcRenderer, remote } from 'electron';
const { getGlobal } = remote;
const mainId = getGlobal('mainId')
const dirname = getGlobal('__dirname')
const deviecMac = getGlobal('device').mac;
在渲染进程中改变:
getGlobal('myField').name = 'code秘密花园';
多个渲染进程共享同一个主进程的全局变量,这样即可达到渲染进程数据共享和传递的效果。