# 🚀Electron进程通信
📅 2023/5/9
由于Electron的主进程运行在node环境中,因此可以通过主进程与渲染进程之间的通信来使用node的相关能力
# 主进程
Electron 的主进程通过ipcMain模块来接收渲染进程传递过来的消息
const {ipcMain} = require('electron')
//接收消息
ipcMain.on('渲染进程的自定义事件名称', (event, ...args) => {
//event为事件对象,...args为事件传递的参数
//处理事件
})
//通过创建窗口的webContents对象发送消息
win.webContents.send('事件名称', ...args)
# 渲染进程
Electron 的渲染进程则通过ipcRenderer模块来接收和发送消息,但是渲染进程默认不能使用ipcRenderer模块,需要在new BrowserWindow()时设置页面允许集成node环境。下方的contextIsolation参数和preload 参数二选一进行设置即可。
//主进程允许渲染进程集成node环境
new BrowserWindow({
...config,
webPreferences: {
nodeIntegration: true, //渲染进程集成node环境
contextIsolation: false, //是否在独立 JavaScript 环境中运行 Electron API和指定的preload 脚本. 默认为 true。需要设置为false来允许页面集成node。如果不设置则需要通过预加载的preload脚本使用node的能力。
preload:'预加载的脚本(必须为绝对路径,例如 path.join(__dirname, "preload.js"))' //在页面运行其他脚本之前预先加载指定的脚本 无论页面是否集成Node, 此脚本都可以访问所有Node API 脚本路径为文件的绝对路径。 具体的使用方法见preload.js脚本的使用。
}
})
const {ipcRenderer} = require('electron')
//接收消息
ipcRenderer.on('主进程的自定义事件名称', (event, ...args) => {
//处理事件
})
//发送消息
ipcRenderer.send('事件名称',...args)
# 同步通信
如果渲染进程需要等到主进程的事件执行完毕后,根据执行结果来决定下一步。我们可以在主进程执行完成后再发送一个执行结果信号给渲染进程来实现,也可以通过同步通信来实现
//渲染进程
const res = ipcRenderer.sendSync('事件名称',...args)
//主进程
ipcMain.on('事件名称', (event, ...args) => {
event.returnValue = res
})
需要注意的是同步通信会阻塞程序运行,直到拿到返回结果。因此建议尽量避免这种方式
# 主进程模块化
为了更好的维护,通常需要对主进程进行模块化。通过commonJS 的模块语法来进行暴露与引用。这时就会面临不同模块之间通信的问题。比如在两个不同的模块建立了两个窗口,模块化后只能拿到自己本身的窗口对象,为了能操作另一个窗口,就需要模块间的通信,目前我使用过两种方法:
- node 自带的events模块
- 第三方库 pubsub-js
//events模块
const EventEmitter = require('events');
const myEmitter = new EventEmitter();
myEmitter.on('myEvent', (data) => {
console.log(`Received data: ${data}`);
});
myEmitter.emit('myEvent', 'Hello, world!');
在使用模块化过程中需要注意的是通过 ipcMain.on 注册的为全局事件,这也就意味的他不会由于窗口的销毁而失效,除非整个程序关闭,如果我们有窗口需要频繁的创建销毁并且恰好创建的时候注册了监听事件,这样在销毁窗口的时候需要手动将其移除,否则当再次创建窗口的时候,他并不会由于注册的事件名称相同而覆盖,而是注册几次就执行几次,这会带来不易发现的问题。
//移除监听事件
ipcMain.removeAllListeners('事件名称')