electron进程通信
存在原因
electron主进程与渲染进程相互独立且不可替换,因此无法直接访问node.js接口或直接访问dom,解决的方式是进程间通信,通信方式:共享内存
多进程模型
概念:
- 进程是CPU资源分配的基本单位,线程是独立运行和独立调度的基本单位(CPU上真正运行的是线程)。
- 进程拥有自己的资源空间,一个进程包含若干个线程,线程与CPU资源分配无关,多个线程共享同一进程内的资源。
- 线程的调度与切换比进程快很多。
electron继承了Chromium 的多进程架构,这使得此框架在架构上非常相似于一个现代的网页浏览器。
多进程好处
- 每开一个标签页相当于新起一个进程,进程与进程之间是任务与任务之间的关系,进程之间相互隔离,这样不会导致一个网站的崩溃影响到整个浏览器,同时也可防止恶意代码的注入
- 拓展性更强,新的第三方插件或者网页相当于一个新的任务,cpu分配资源更加灵活
electron进程
主进程
主进程在node.js环境中运行,因此具有
require
模块和使用所有node.js API
的能力。
主进程的主要目的是使用 BrowserWindow
模块创建和管理应用程序窗口。
BrowserWindow
类的每个实例创建一个应用程序窗口,且在单独的渲染器进程中加载一个网页。 您可从主进程用 window 的 webContent
对象与网页内容进行交互。
渲染进程
每个 Electron 应用都会为每个打开的 BrowserWindow
( 与每个网页嵌入 ) 生成一个单独的渲染器进程。 洽如其名,渲染器负责 渲染 网页内容。 所以实际上,运行于渲染器进程中的代码是须遵照网页标准的 (至少就目前使用的 Chromium 而言是如此) 。
electron进程间通信
IPC通道
在 Electron 中,进程使用 ipcMain
和 ipcRenderer
模块,通过开发人员定义的“通道”传递消息来进行通信。 这些通道是 任意 (您可以随意命名它们)和 双向 (您可以在两个模块中使用相同的通道名称)的。
分别对应主进程和渲染进程
上下文隔离
上下文隔离功能将确保您的
预加载
脚本 和 Electron的内部逻辑 运行在所加载的webcontent
网页 之外的另一个独立的上下文环境里。
安全
// ❌ 错误使用
contextBridge.exposeInMainWorld('myAPI', {
send: ipcRenderer.send
})
它直接暴露了一个没有任何参数过滤的高等级权限 API 。 这将允许任何网站发送任意的 IPC 消息,这不会是你希望发生的。 相反,暴露进程间通信相关 API 的正确方法是为每一种通信消息提供一种实现方法。
// ✅ 正确使用
contextBridge.exposeInMainWorld('myAPI', {
loadPreferences: () => ipcRenderer.invoke('load-prefs')
})
进程通信模式
渲染器进程到主进程(单向)
-
main.js
中设置监听器,核心API
为ipcMain.on() //e.g function handleSetTitle (event, title) { const webContents = event.sender const win = BrowserWindow.fromWebContents(webContents) win.setTitle(title) } app.whenReady().then(() => { ipcMain.on('set-title', handleSetTitle) createWindow() }
-
通过预加载脚本
preload.js
暴露,核心API
为ipcRenderer.send() // const { contextBridge, ipcRenderer } = require('electron') contextBridge.exposeInMainWorld('electronAPI', { setTitle: (title) => ipcRenderer.send('set-title', title) })
-
在
renderer.js
中调用
过程: 主进程->预加载脚本->渲染进程->todo
渲染器进程到主进程(双向)(官方建议方式)
双向 IPC 的一个常见应用是从渲染器进程代码调用主进程模块并等待结果。 这可以通过将
ipcRenderer.invoke
与ipcMain.handle
搭配使用来完成。
-
主进程使用
ipcMain.handle
监听事件说明: 主进程创建函数,调用node环境下
API
,每当渲染器进程通过dialog:openFile
通道发送ipcRender.invoke
消息时,此函数被用作一个回调。 然后,返回值将作为一个 Promise 返回到最初的invoke
调用。 -
预加载脚本
preload.js
,暴露函数 -
渲染进程
rederer.js
中调用
过程: 主进程->预加载脚本->渲染进程->dom->主进程->todo
主进程到渲染器进程
将消息从主进程发送到渲染器进程时,需要指定是哪一个渲染器接收消息。 消息需要通过其
WebContents
实例发送到渲染器进程。 此WebContents
实例包含一个send
方法,其使用方式与ipcRenderer.send
相同。
-
使用
webContents
模块发送消息//该实例创建了一个自定义的menu菜单 const {app, BrowserWindow, Menu, ipcMain} = require('electron') const menu = Menu.buildFromTemplate([ { label: app.name, submenu: [ { click: () => mainWindow.webContents.send('update-counter', 1), label: 'Increment', }, { click: () => mainWindow.webContents.send('update-counter', -1), label: 'Decrement', } ] } ]) Menu.setApplicationMenu(menu) //此示例将在menu中创建一个选项卡,控制dom界面数字的增减 //选项卡点击后发送消息到指定的渲染器进程
-
在预加载器
preload.js
中暴露const { ipcRenderer } = require('electron') window.addEventListener('DOMContentLoaded', () => { const counter = document.getElementById('counter') ipcRenderer.on('update-counter', (_event, value) => { const oldValue = Number(counter.innerText) const newValue = oldValue + value counter.innerText = newValue }) })
-
渲染器(renderer.js)调用
渲染器进程到渲染器进程
没有直接的方法可以使用 ipcMain
和 ipcRenderer
模块在 Electron 中的渲染器进程之间发送消息。 为此,您有两种选择:
- 将主进程作为渲染器之间的消息代理。 这需要将消息从一个渲染器发送到主进程,然后主进程将消息转发到另一个渲染器。
- 从主进程将一个 MessagePort 传递到两个渲染器。 这将允许在初始设置后渲染器之间直接进行通信。