学习 Electron - 进程通信
Electron 进程通信
Electron 的主进程和渲染进程有着清楚分工且不可互换。
因此,从渲染进程直接访问 Node 接口或 从主进程访问 DOM 都是不可能的。
解决此问题的方法就是使用 进程间通信(IPC,inter-process communication)。
渲染器进程到主进程(单向)
使用 ipcRenderer.send API,然后使用 ipcMain.on 接受。
常用于从 Web 内容调用主进程 API。
// preload.js
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
setTitle: (title) => ipcRenderer.send('set-title', title)
})
// main.js
const { app, BrowserWindow, ipcMain } = require('electron')
const path = require('path')
function createWindow () {
const mainWindow = new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
ipcMain.on('set-title', (event, title) => {
const webContents = event.sender
const win = BrowserWindow.fromWebContents(webContents)
win.setTitle(title)
})
mainWindow.loadFile('index.html')
}
app.whenReady().then(() => {
createWindow()
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})
// renderer.js
const setButton = document.getElementById('btn')
const titleInput = document.getElementById('title')
setButton.addEventListener('click', () => {
const title = titleInput.value
window.electronAPI.setTitle(title)
})
渲染器进程到主进程(双向)
常应用于渲染器进程调用主进程模块并等待结果。
通过 ipcMain 和 ipcRenderer 模块进行进程间通信。
从渲染进程向主进程发送消息,可使用 ipcMain.handle 设置一个主进程处理程序,然后再预加载脚本中暴露一个 ipcRedner.invoke 的函数触发该 handle。
// preload.js
const { contextBridge, ipcRenderer } = require('electron')
context.exposeInMainWorld('versions', {
...,
// 此处使用辅助函数包裹 ipcRenderer.invoke('ping') 调用
// 是因为这样会让渲染器直接向主进程发送任意的 IPC 信息,导致问题
ping: () => ipcRenderer.invoke('ping')
})
// main.js
const { app, BorwserWindow, ipcMain } = require('electron')
app.whenReady().then(() => {
ipcMain.handle('ping', () => 'pong')
// ...
})
// renderer.js
const func = async () => await window.versions.ping()
func()
主进程到渲染器进程
首先需要指定哪一个渲染器接收数据。
数据通过 WebContents 实例发送。
此实例包含一个 send 方法,使用方法与 ipcRenderer.send 相同。
// main.js
const { app, BrowserWindow, Menu, ipcMain } = require('electron')
const path = require('path')
function createWindow () {
const mainWindow = new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
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)
mainWindow.loadFile('index.html')
// Open the DevTools.
mainWindow.webContents.openDevTools()
}
app.whenReady().then(() => {
ipcMain.on('counter-value', (_event, value) => {
console.log(value) // will print value to Node console
})
createWindow()
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})
// preload.js
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
handleCounter: (callback) => ipcRenderer.on('update-counter', callback)
})
// renderer.js
const counter = document.getElementById('counter')
window.electronAPI.handleCounter((event, value) => {
const oldValue = Number(counter.innerText)
const newValue = oldValue + value
counter.innerText = newValue
event.sender.send('counter-value', newValue)
})
渲染器进程到渲染器进程
没有直接方法。可选方法:
- 将主进程作为中继器进行转发。
- 从主进程将一个 MessagePort 传递到两个渲染器。将允许初始设置后渲染器之间直接进行通信。
对象序列化
Electron 的 IPC 实现使用 HTML 标准 结构化克隆算法 来序列化进程间传递对象。因此,只有某些类型对象可以通过 IPC 通道传递。
特别是 DOM 对象, Node 和 Electron 中由 C++ 类支持的对象无法进行结构化克隆序列化。
学习 Electron - 进程通信
http://localhost:8080/archives/fed1b5fb-f304-438b-b796-80dc48e620b8