Electron进程间通信
IPC通道
在 Electron 中,进程使用 ipcMain 和 ipcRenderer 模块,通过开发人员定义的“通道”传递消息来进行通信。 这些通道是 任意 (您可以随意命名它们)和 双向 (您可以在两个模块中使用相同的通道名称)的。
模式 1:渲染器进程到主进程(单向)
要将单向 IPC 消息从渲染器进程发送到主进程,您可以使用 ipcRenderer.send API 发送消息,然后使用 ipcMain.on API 接收
通过预加载脚本暴露API,让我们可以在渲染进程发送消息到主进程
preload.js
const {contextBridge, ipcRenderer} = require('electron')
contextBridge.exposeInMainWorld('controlAPI', {
setScreen: (status) => ipcRenderer.send('set-screen', status),
})
在上面我们暴露了一个 setScreen 方法,我们可以在渲染进程中调用它
index.html
const setScreen = (status: string) => {
window.controlAPI.setScreen(status)
}
当我们在渲染进程触发 setScreen 方法,在主进程中我们通过 ipcMian.on 接收
mian.js
ipcMain.on('set-screen', (event, args) => {
switch (args) {
case 'max':
win.isMaximized() ? win.unmaximize() : win.maximize()
break;
case 'min':
win.minimize()
break;
case 'close':
win.close()
break;
default:
break;
}
})
模式 2:主进程到渲染器进程
将消息从主进程发送到渲染器进程时,需要指定是哪一个渲染器接收消息。 消息需要通过其 WebContents 实例发送到渲染器进程。 此 WebContents 实例包含一个 send 方法,其使用方式与 ipcRenderer.send 相同,渲染器进程通过 ipcRenderer.on API接收
通过预加载脚本暴露API,让我们可以在渲染进程监听主进程发送的消息
preload.js
const {contextBridge, ipcRenderer} = require('electron')
contextBridge.exposeInMainWorld('controlAPI', {
checkUpdate: (callback) => ipcRenderer.on('check-update', callback)
})
在上面我们暴露了一个 checkUpdate 方法,我们可以在渲染进程中调用它
main.js
const sendUpdateMessage = (win, key, value) => {
win && win.webContents.send('check-update', {key, value})
}
autoUpdater.checkForUpdates();
/*New Update Available*/
autoUpdater.on("update-available", (info) => {
let pth = autoUpdater.downloadUpdate();
sendUpdateMessage(win, "update_available", "update_available")
});
autoUpdater.on("update-not-available", (info) => {
sendUpdateMessage(win, "update_not_available", "update_not_available")
});
/*Download Completion Message*/
autoUpdater.on("update-downloaded", (info) => {
sendUpdateMessage(win, "update_downloaded", "update_downloaded")
autoUpdater.quitAndInstall()
app.quit()
});
autoUpdater.on('download-progress', (progress) => {
sendUpdateMessage(win, "download_progress", progress.percent)
})
autoUpdater.on("error", (error) => {
sendUpdateMessage(win, "error", error || 'Error checking for updates')
});
在主进程中我们检测应用更新,检测到什么信息就通过 WebContents.send 发送消息到渲染进程
index.html
window.controlAPI.checkUpdate((event, data) => {
switch (data.key) {
case "download_progress":
percent.value = data.value
break;
case "update_available":
snackbar.info(t('update_available'))
break;
case "update_downloaded":
snackbar.success(t('update_downloaded'))
break;
case "error":
snackbar.error(data.value)
break;
default:
break;
}
})
在渲染进程我们通过预加载脚本暴露的API监听着主进程发送过来的消息并执行操作
模式 3:渲染器进程到主进程(双向)
双向 IPC 的一个常见应用是从渲染器进程代码调用主进程模块并等待结果。 这可以通过将 ipcRenderer.invoke 与 ipcMain.hendle 搭配使用来完成。
preload.js
const { contextBridge, ipcRenderer } = require('electron/renderer')
contextBridge.exposeInMainWorld('electronAPI', {
openFile: () => ipcRenderer.invoke('dialog:openFile')
})
在预加载脚本中,我们暴露了一个单行的 openFile 函数,它调用并返回 ipcRenderer.invoke('dialog:openFile') 的值
main.js
async function handleFileOpen () {
const { canceled, filePaths } = await dialog.showOpenDialog()
if (!canceled) {
return filePaths[0]
}
}
app.whenReady().then(() => {
ipcMain.handle('dialog:openFile', handleFileOpen)
createWindow()
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
在主进程中,我们将创建一个 handleFileOpen 函数,它调用 dialog.showOpenDialog 并返回用户选择的文件路径值。 每当渲染器进程通过 dialog:openFile 通道发送 ipcRenderer.invoke 消息时,此函数被用作一个回调。 然后,返回值将作为一个 Promise 返回到最初的 invoke 调用。
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
<title>Dialog</title>
</head>
<body>
<button type="button" id="btn">Open a File</button>
File path: <strong id="filePath"></strong>
<script src='./renderer.js'></script>
</body>
</html>
用户界面包含一个 #btn 按钮元素,将用于触发我们的预加载 API,以及一个 #filePath 元素,将用于显示所选文件的路径
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
})
在上面的代码片段中,我们监听 #btn 按钮的点击,并调用 windos.electronAPI.openFile() API 来激活原生的打开文件对话框。 然后我们在 #filePath 元素中显示选中文件的路径