Electron上下文隔离进程间的四种通信方式示例详解

文章目录

  • 一、Electron 进程之间的通信
  • 二、上下文隔离的进程间通信方式有四种,示例如下
    • 1、 渲染器到主进程(单向)
      • (1)主进程:electron/main.js
      • (2)预加载文件:electron/preload.js
      • (3)渲染进程文件:xxx.vue
    • 2、渲染器进程到主进程(双向)
      • (1)主进程:electron/main.js
      • (2)预加载文件:electron/preload.js
      • (3)渲染进程文件:xxx.vue
    • 3、主进程到渲染器进程(双向)
      • (1) 主进程通知渲染进程【win.webContents.send 和 ipcRenderer.on】
        • ① 主进程:electron/main.js
        • ② 预加载文件:electron/preload.js
        • ③ 渲染进程文件:xxx.vue
      • (2)如果渲染进程收到消息后想要回复主进程【event.sender.send 和 ipcMain.on】
        • ① 渲染进程文件:xxx.vue
        • ② 主进程:electron/main.js
    • 4、渲染器进程到渲染器进程
      • 主进程 代码
      • 预加载文件 代码
      • 渲染进程1 代码 (发送)
      • 渲染进程2 代码 (接收)

一、Electron 进程之间的通信

Electron进程通信大致可分为主进程和渲染进程两种:

  • 主进程:指的就是 node.js 进程,node.js 只有一个进程。
  • 渲染进程:可以简单理解为一个 Chromium 的 web 页面,可以有多个渲染进程。

ipc通信就是在主进程和渲染进程之间通信
​​​推荐使用上下文隔离渲染器进程进行通信。这种方式的好处是无需在渲染进程中直接使用 ​​ipcRenderer ​​​发送消息,当我们使用​​ vue​​​ 或者其他前端框架开发界面时,上下文隔离方式使用起来更加方便,基本上感受不到 ​​electron ​​对前端框架的影响。

二、上下文隔离的进程间通信方式有四种,示例如下

示例背景如下
在vue项目中创建一个electron文件夹

  • main.js(主进程文件)
  • preload.js(预加载脚本)

1、 渲染器到主进程(单向)

ipcMain.on 和 ipcRenderer.send
示例描述:从 渲染进程中操作主进程的关闭操作,也就是从渲染进程中关闭electron

(1)主进程:electron/main.js

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;
    }
})

(2)预加载文件:electron/preload.js

//向渲染器进程暴露一个全局的 window.electronAPI 变量。
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
    operationWindow: (operationType) => ipcRenderer.send('operation-window', operationType),
})

(3)渲染进程文件:xxx.vue

2、渲染器进程到主进程(双向)

ipcMain.handle 和 ipcRenderer.invoke
示例描述:从渲染进程中 调用 主进程的弹窗选择文件后返回文件路径给 渲染进程

  • 在主进程中,我们将创建一个 ​​handleFileOpen()​​​ 函数,它调用electron的 ​​dialog.showOpenDialog​​​ 并返回用户选择的文件路径值。
  • 每当渲染器进程通过 ​​dialog:openFile​​​ 通道发送 ​​ipcRender.invoke​​​ 消息时,此函数被用作一个回调。然后,返回值将作为一个 Promise 返回到最初的 ​​invoke​​ 调用。

(1)主进程:electron/main.js

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)

(2)预加载文件:electron/preload.js

import { contextBridge, ipcRenderer } from 'electron'

contextBridge.exposeInMainWorld('electronAPI', {
  openFile: () => ipcRenderer.invoke('dialog:openFile')
})

(3)渲染进程文件:xxx.vue

3、主进程到渲染器进程(双向)

win.webContents.send 和 ipcRenderer.on
event.sender.send 和 ipcMain.on
示例描述:从主进程的托盘上下文菜单每次被点击都通知 渲染进程, 渲染进程接收后,告诉主进程收到了。

  • 将消息从主进程发送到渲染器进程时,需要指定是哪一个渲染器接收消息。
  • 消息需要通过其 ​​WebContents​​​ 实例发送到渲染器进程。此 WebContents 实例包含一个 ​​send​​​ 方法,其使用方式与 ​​ipcRenderer.send​​ 相同。

(1) 主进程通知渲染进程【win.webContents.send 和 ipcRenderer.on】

① 主进程:electron/main.js
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);//显示系统菜单
});
② 预加载文件:electron/preload.js
//向渲染器进程暴露一个全局的 window.electronAPI 变量。
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
    onUpdateCounter: (callback) => ipcRenderer.on('update-counter', callback);
})
③ 渲染进程文件:xxx.vue

(2)如果渲染进程收到消息后想要回复主进程【event.sender.send 和 ipcMain.on】

① 渲染进程文件:xxx.vue
//在渲染器进程中,使用 ​​event​​​ 参数,通过 ​​counter-value​​ 通道将回复发送回主进程。

② 主进程:electron/main.js
//在主进程中,监听 ​​counter-value​​ 事件并适当地处理它们。
const { ipcMain} = require('electron');
ipcMain.on('counter-value',function(event,value){
    console.log("接收渲染进程发过来的消息===",value)
})

4、渲染器进程到渲染器进程

没有直接的方法可以使用 ​​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),//把消息通知到指定窗口
})

渲染进程1 代码 (发送)

if(window.electronAPI && window.electronAPI.postMessage){
	window.electronAPI.postMessage('childWindow',{test:"发送给子窗口的数据~~"});
}

渲染进程2 代码 (接收)

if(window.electronAPI && window.electronAPI.getMessage){
		window.electronAPI.getMessage((event,params) => {
			console.log(params)//打印结果:{test:"发送给子窗口的数据~~"}
		})
	}

(2) 从主进程将一个 MessagePort 传递到两个渲染器。这将允许在初始设置后渲染器之间直接进行通信。

你可能感兴趣的:(electron,javascript,前端)