Electron 进程通信篇
一、Electron进程间如何通信?
进程间通信 (IPC) 是在 Electron 中构建功能丰富的桌面应用程序的关键部分之一。 由于主进程和渲染器进程在 Electron 的进程模型具有不同的职责,因此 IPC 是执行许多常见任务的唯一方法,例如从 UI 调用原生 API 或从原生菜单触发 Web 内容的更改。
1.渲染器进程到主进程(单向)
要将单向 IPC 消息从渲染器进程发送到主进程,您可以使用 ipcRenderer.send API 发送消息,然后使用 ipcMain.on API 接收。
Main.js 代码如下(示例):
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')
}
Preload.js 代码如下(示例):
在预加载脚本中添加以下代码,向渲染器进程暴露一个全局的 window.electronAPI 变量。
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
setTitle: (title) => ipcRenderer.send('set-title', title)
})
renderer.js 代码如下(示例):
利用从预加载脚本中暴露的 window.electronAPI 功能。
const setButton = document.getElementById('btn')
const titleInput = document.getElementById('title')
setButton.addEventListener('click', () => {
const title = titleInput.value
window.electronAPI.setTitle(title)
});
2.渲染器进程到主进程(双向)
双向 IPC 的一个常见应用是从渲染器进程代码调用主进程模块并等待结果。 这可以通过将 ipcRenderer.invoke 与 ipcMain.handle 搭配使用来完成。
Main.js 代码如下(示例):
async function handleFileOpen() {
const { canceled, filePaths } = await dialog.showOpenDialog()
if (canceled) {
return
} else {
return filePaths[0]
}
}
app.whenReady().then(() => {
ipcMain.handle('dialog:openFile', handleFileOpen)
createWindow()
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
Preload.js 代码如下(示例):
在预加载脚本中,我们暴露一个单行的 openFile 函数,它调用并返回 ipcRenderer.invoke(‘dialog:openFile’) 的值。
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
openFile: () => ipcRenderer.invoke('dialog:openFile')
})
Renderer.js 代码如下(示例):
const btn = document.getElementById('btn')
const filePathElement = document.getElementById('filePath')
btn.addEventListener('click', async () => {
const filePath = await window.electronAPI.openFile()
filePathElement.innerText = filePath
})
注意:ipcRenderer.invoke API 是在 Electron 7 中添加的,作为处理渲染器进程中双向 IPC 的一种开发人员友好的方式。 但这种 IPC 模式存在几种替代方法。
使用 ipcRenderer.send,单向通信的 ipcRenderer.send API 也可用于双向通信。 这是在 Electron 7 之前通过 IPC 进行异步双向通信的推荐方式。
Preload.js 代码如下:
// 将这段代码暴露给渲染器进程
const { ipcRenderer } = require('electron')
ipcRenderer.on('asynchronous-reply', (_event, arg) => {
console.log(arg) // 在 DevTools 控制台中打印“pong”
})
ipcRenderer.send('asynchronous-message', 'ping')
Main.js 代码如下:
// 将这段代码暴露给渲染器进程
ipcMain.on('asynchronous-message', (event, arg) => {
console.log(arg) // 在 Node 控制台中打印“ping”
// 作用如同 `send`,但返回一个消息
// 到发送原始消息的渲染器
event.reply('asynchronous-reply', 'pong')
})
3.主进程到渲染器进程
将消息从主进程发送到渲染器进程时,需要指定是哪一个渲染器接收消息。 消息需要通过其 WebContents 实例发送到渲染器进程。 此 WebContents 实例包含一个 send 方法,其使用方式与 ipcRenderer.send 相同。
Main.js 代码如下:
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)
Preload.js 代码如下:
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
})
})
Index.html 代码如下:
window.electronAPI.onUpdateCounter((_event, value) => {
const oldValue = Number(counter.innerText)
const newValue = oldValue + value
counter.innerText = newValue
})
可选:返回一个回复
在渲染器进程中,使用 event 参数,通过 counter-value 通道将回复发送回主进程。
renderer.js代码如下:
window.electronAPI.onUpdateCounter((event, value) => {
const oldValue = Number(counter.innerText)
const newValue = oldValue + value
counter.innerText = newValue
event.sender.send('counter-value', newValue)
})
Main.html 代码如下:
ipcMain.on('counter-value', (_event, value) => {
console.log(value) // 将打印到 Node 控制台
})
总结
以上就是Electron子进程之间的通信方式。