Electron进程通信大致可分为主进程和渲染进程两种:
- 主进程:指的就是 node.js 进程,node.js 只有一个进程。
- 渲染进程:可以简单理解为一个 Chromium 的 web 页面,可以有多个渲染进程。
ipc通信就是在主进程和渲染进程之间通信
推荐使用上下文隔离渲染器进程进行通信。这种方式的好处是无需在渲染进程中直接使用 ipcRenderer 发送消息,当我们使用 vue 或者其他前端框架开发界面时,上下文隔离方式使用起来更加方便,基本上感受不到 electron 对前端框架的影响。
示例背景如下
在vue项目中创建一个electron文件夹
- main.js(主进程文件)
- preload.js(预加载脚本)
ipcMain.on 和 ipcRenderer.send
示例描述:从 渲染进程中操作主进程的关闭操作,也就是从渲染进程中关闭electron
const { ipcMain } = require('electron');
ipcMain.on('operation-window',function(event,operationType){
switch(operationType){
case "max"://窗口 最大化
win.maximize()
break;
case 'restoreDown'://窗口 向下还原
win.unmaximize()
break;
case "min"://窗口 最小化
win.minimize()
break;
case 'close'://窗口 关闭
win.close()
break;
}
})
//向渲染器进程暴露一个全局的 window.electronAPI 变量。
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
operationWindow: (operationType) => ipcRenderer.send('operation-window', operationType),
})
ipcMain.handle 和 ipcRenderer.invoke
示例描述:从渲染进程中 调用 主进程的弹窗选择文件后返回文件路径给 渲染进程
- 在主进程中,我们将创建一个 handleFileOpen() 函数,它调用electron的 dialog.showOpenDialog 并返回用户选择的文件路径值。
- 每当渲染器进程通过 dialog:openFile 通道发送 ipcRender.invoke 消息时,此函数被用作一个回调。然后,返回值将作为一个 Promise 返回到最初的 invoke 调用。
const { ipcMain,dialog } = require('electron');
async function handleFileOpen() {
const { canceled, filePaths } = await dialog.showOpenDialog()//调用electron的打开文件弹窗
if (canceled) {
return
} else {
return filePaths[0] // 返回文件名给渲染进程
}
}
ipcMain.handle('dialog:openFile',handleFileOpen)
import { contextBridge, ipcRenderer } from 'electron'
contextBridge.exposeInMainWorld('electronAPI', {
openFile: () => ipcRenderer.invoke('dialog:openFile')
})
win.webContents.send 和 ipcRenderer.on
event.sender.send 和 ipcMain.on
示例描述:从主进程的托盘上下文菜单每次被点击都通知 渲染进程, 渲染进程接收后,告诉主进程收到了。
- 将消息从主进程发送到渲染器进程时,需要指定是哪一个渲染器接收消息。
- 消息需要通过其 WebContents 实例发送到渲染器进程。此 WebContents 实例包含一个 send 方法,其使用方式与 ipcRenderer.send 相同。
const { ipcMain, Tray, Menu } = require('electron');
//系统托盘
let appTray = new Tray(iconPath)
const trayContextMenu = Menu.buildFromTemplate([
{
label: "打开安信",
click:() => {
win.webContents.send('update-counter',1)//每次点击都告诉渲染进程,我被点击了一次
}
},
])
//系统托盘的提示文本
appTray.setToolTip('安信');
//点击系统托盘打开窗口
appTray.on('click',() => {
win.show();
});
//系统托盘右键点击事件
appTray.on('right-click',() => {
appTray.popUpContextMenu(trayContextMenu);//显示系统菜单
});
//向渲染器进程暴露一个全局的 window.electronAPI 变量。
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
onUpdateCounter: (callback) => ipcRenderer.on('update-counter', callback);
})
//在渲染器进程中,使用 event 参数,通过 counter-value 通道将回复发送回主进程。
//在主进程中,监听 counter-value 事件并适当地处理它们。
const { ipcMain} = require('electron');
ipcMain.on('counter-value',function(event,value){
console.log("接收渲染进程发过来的消息===",value)
})
没有直接的方法可以使用 ipcMain 和 ipcRenderer 模块在 Electron 中的渲染器进程之间发送消息。为此,我们有两种选择:
(1) 将主进程作为渲染器之间的消息代理。这需要将消息从一个渲染器发送到主进程,然后主进程将消息转发到另一个渲染器。
/**
* 接收渲染进程 转发消息 的通知
* @param {Object} event
* @param {String} windowName 目标窗口名称
* @param {Array} params 发送参数
*/
ipcMain.on('post-message',function(event,windowName,params) {
if(windows[windowName]){
windows[windowName].webContents.send('get-message',params);//转发给指定窗口
}
})
//向渲染器进程暴露一个全局的 window.electronAPI 变量。
const { contextBridge, ipcRenderer, app } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
postMessage: (windowName,params) => ipcRenderer.send('post-message',windowName,params),//跨窗口发送消息,接收渲染进程的转发消息的通知
getMessage: (callback) => ipcRenderer.on('get-message', callback),//把消息通知到指定窗口
})
if(window.electronAPI && window.electronAPI.postMessage){
window.electronAPI.postMessage('childWindow',{test:"发送给子窗口的数据~~"});
}
if(window.electronAPI && window.electronAPI.getMessage){
window.electronAPI.getMessage((event,params) => {
console.log(params)//打印结果:{test:"发送给子窗口的数据~~"}
})
}
(2) 从主进程将一个 MessagePort 传递到两个渲染器。这将允许在初始设置后渲染器之间直接进行通信。