Electron 中 IPC(進(jìn)程間通信)模式
在 Electron 中,ipcRenderer.invoke/ipcMain.handle 和 ipcRenderer.send/ipcMain.on 是兩種不同的 IPC(進(jìn)程間通信)模式,主要區(qū)別在于 通信方向 和 返回結(jié)果的方式。
ipcRenderer.invoke/ipcMain.handle
渲染進(jìn)程通過(guò) ipcRenderer.invoke 向主進(jìn)程發(fā)送通信請(qǐng)求,并異步等待主進(jìn)程返回結(jié)果;主進(jìn)程通過(guò) ipcMain.handle 監(jiān)聽(tīng)處理并返回一個(gè) Promise 對(duì)象。該通信模式特點(diǎn)如下:
- 雙向通信,主進(jìn)程返回一個(gè) Promise,可以通過(guò) async/await 或 .then() 獲取結(jié)果。
- 主進(jìn)程通過(guò) ipcMain.handle 處理請(qǐng)求,并直接返回結(jié)果。
- 適合需要等待主進(jìn)程處理結(jié)果的場(chǎng)景(例如:讀取文件、數(shù)據(jù)庫(kù)操作)。
- ipcRenderer.invoke 與 ipcMain.handle 一般成對(duì)使用。
// preload.js 預(yù)加載腳本
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
openFile: () => ipcRenderer.invoke('open-file'), // 渲染進(jìn)程觸發(fā)通信的頻道名稱open-file,主進(jìn)程監(jiān)聽(tīng)open-file頻道并返回文件路徑
})
// main.js 主進(jìn)程
const { app, BrowserWindow, Menu, ipcMain, dialog } = require('electron/main')
const path = require('node:path')
// 創(chuàng)建瀏覽器窗口
const createWindow = () => {
const win = new BrowserWindow({
width: 800, // 彈窗寬度
height: 600, // 彈窗高度
webPreferences: {
// sandbox: false, // 是否開(kāi)啟沙盒模式
// nodeIntegration: false, // 是否開(kāi)啟node集成
// contextIsolation: true, // 是否開(kāi)啟上下文隔離
preload: path.join(__dirname, 'preload.js'), // 預(yù)加載腳本
},
})
// 把html文件加載到彈窗中
win.loadFile('index.html')
}
app.whenReady().then(() => {
createWindow()
// 監(jiān)聽(tīng)渲染進(jìn)程的 open-file 頻道,顯示文件選擇框
ipcMain.handle('open-file', async () => {
const { canceled, filePaths } = await dialog.showOpenDialog()
if (canceled) {
return
} else {
return filePaths[0]
}
})
})
// render.js 渲染腳本
window.addEventListener('DOMContentLoaded', () => {
const electronAPI = window.electronAPI
if (electronAPI?.openFile) {
const openBtn = document.getElementById('open-btn')
const pathElement = document.getElementById('filePath')
openBtn.addEventListener('click', async () => {
const filePath = await electronAPI.openFile()
pathElement.innerText = filePath
})
}
})
ipcRenderer.send/ipcMain.on
渲染進(jìn)程通過(guò) ipcRenderer.send 向主進(jìn)程發(fā)送單向消息,不期待主進(jìn)程返回結(jié)果。主進(jìn)程通過(guò) ipcMain.on 監(jiān)聽(tīng)消息,如果需要響應(yīng)渲染進(jìn)程,則需要手動(dòng)通過(guò) event.reply 返回結(jié)果。該通信模式特點(diǎn)如下:
- 單向通信,發(fā)送后不等待主進(jìn)程響應(yīng)。
- 主進(jìn)程通過(guò) ipcMain.on 監(jiān)聽(tīng)消息,需要手動(dòng)通過(guò) event.reply 返回結(jié)果(如果需要)。
- 適合不需要立即獲取結(jié)果的場(chǎng)景(例如:通知主進(jìn)程執(zhí)行某個(gè)操作)。
// preload.js 預(yù)加載腳本
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
send: (channel, data) => {
// 白名單通道
let validChannels = ['toMain']
if (validChannels.includes(channel)) {
// 渲染進(jìn)程出發(fā)send,主進(jìn)程返回toRenderer
ipcRenderer.send(channel, data)
}
},
})
// 渲染進(jìn)程監(jiān)聽(tīng)主進(jìn)程返回的信息
ipcRenderer.on('toRenderer', (event, arg) => {
console.log(arg) // 打印主進(jìn)程傳遞過(guò)來(lái)的參數(shù)
document.getElementById('toRender').innerText = arg
})
// main.js 主進(jìn)程
const { app, BrowserWindow, Menu, ipcMain, dialog } = require('electron/main')
const path = require('node:path')
// 創(chuàng)建瀏覽器窗口
const createWindow = () => {
const win = new BrowserWindow({
width: 800, // 彈窗寬度
height: 600, // 彈窗高度
webPreferences: {
// sandbox: false, // 是否開(kāi)啟沙盒模式
// nodeIntegration: false, // 是否開(kāi)啟node集成
// contextIsolation: true, // 是否開(kāi)啟上下文隔離
preload: path.join(__dirname, 'preload.js'), // 預(yù)加載腳本
},
})
// 把html文件加載到彈窗中
win.loadFile('index.html')
}
app.whenReady().then(() => {
createWindow()
// 主進(jìn)程監(jiān)聽(tīng)渲染進(jìn)程的 toMain 頻道,并返回 toRenderer 頻道
ipcMain.on('toMain', (event, arg) => {
console.log(arg) // 打印渲染進(jìn)程傳遞過(guò)來(lái)的參數(shù)
event.reply('toRenderer', "I'm fine, thank you.")
})
})
// render.js 渲染腳本
window.addEventListener('DOMContentLoaded', () => {
const setButton = document.getElementById('send-btn')
setButton.addEventListener('click', () => {
if (window.electronAPI?.send) {
window.electronAPI.send('toMain', 'How are you?')
}
})
})
// html
<div class="demo-5 demo-box">
<p>
向主進(jìn)程發(fā)送消息:How are you?
<button type="button" id="send-btn">toMain</button>
</p>
<p>
主進(jìn)程響應(yīng):
<span id="toRender"></span>
</p>
</div>
注意事項(xiàng)
版本兼容性:ipcRenderer.invoke 和 ipcMain.handle 從 Electron 7 開(kāi)始支持。舊版本中可以使用 ipcRenderer.send + ipcMain.on + event.reply 實(shí)現(xiàn)類似功能。
錯(cuò)誤處理:invoke/handle 的錯(cuò)誤會(huì)自動(dòng)傳遞到渲染進(jìn)程的 Promise.catch 中。在 send/on 中需要手動(dòng)處理錯(cuò)誤。
通信通道命名:避免通道名(通信頻道名稱)沖突(例如:為 invoke 和 send 使用不同的通道名)。

浙公網(wǎng)安備 33010602011771號(hào)