单向通讯( 渲染进程 -> 主进程 )
// 主进程 main.jsipcMain.on('set-title', (event, title) => {})// 预加载脚本 preload.js
const { contextBridge, ipcRenderer } = require('electron/renderer')
contextBridge.exposeInMainWorld('electronAPI', {setTitle: (title) => ipcRenderer.send('set-title', title)
})// 实际调用,渲染进程
setButton.addEventListener('click', () => {const title = titleInput.valuewindow.electronAPI.setTitle(title)
})
单向通讯( 主进程 -> 渲染进程 )
将消息从主进程发送到渲染器进程时,需要指定是哪一个渲染器接收消息。消息需要通过WebContents实例发送到渲染器进程。此WebContents实例包含一个send方法,其使用方式与ipcRenderer.send相同。
对于从主进程到渲染器进程的 IPC,没有与
ipcRenderer.invoke
等效的 API。 不过,您可以从ipcRenderer.on
回调中将回复发送回主进程(通过 单向通讯 渲染进程 -> 主进程)。
// 主进程 main.js
function createWindow () {const mainWindow = new BrowserWindow({webPreferences: {preload: path.join(__dirname, 'preload.js')}})mainWindow.webContents.send('update-counter', 1)mainWindow.loadFile('index.html')
}// 预加载脚本 preload.js
const { contextBridge, ipcRenderer } = require('electron/renderer')
contextBridge.exposeInMainWorld('electronAPI', {onUpdateCounter: (callback) => ipcRenderer.on('update-counter', (_event, value) => callback(value)),
})// 实际调用,渲染进程
const counter = document.getElementById('counter')
window.electronAPI.onUpdateCounter((value) => {const oldValue = Number(counter.innerText)const newValue = oldValue + valuecounter.innerText = newValue.toString()
})
双向通讯(渲染进程 <-> 主进程)
// 方法1:渲染进程请求,主进程方法,并等待其结果;(ipcRenderer.invoke 和 ipcMain.handle 配对使用)
// 示例:从渲染器进程打开一个原生的文件对话框,并返回所选文件的路径。// 主进程 main.js
async function handleFileOpen () {const { canceled, filePaths } = await dialog.showOpenDialog()if (!canceled) {return filePaths[0]}
}ipcMain.handle('dialog:openFile', handleFileOpen)// 预加载脚本 preload.js
const { contextBridge, ipcRenderer } = require('electron/renderer')
contextBridge.exposeInMainWorld('electronAPI', {openFile: () => ipcRenderer.invoke('dialog:openFile')
})// 实际调用,渲染进程
const btn = document.getElementById('btn')
const filePathElement = document.getElementById('filePath')
btn.addEventListener('click', async () => {const filePath = await window.electronAPI.openFile()filePathElement.innerText = filePath
})
// 方法2: 单向通信的 ipcRenderer.send API 也可用于双向通信。 这是在 Electron 7 之前通过 IPC 进行异步双向通信的推荐方式。// 主进程 main.js
ipcMain.on('asynchronous-message', (event, arg) => {console.log(arg) // 在 Node 控制台中打印“ping”// 作用如同 `send`,但返回一个消息,到发送原始消息的渲染器event.reply('asynchronous-reply', 'pong')
})// 预加载脚本 preload.js
const { ipcRenderer } = require('electron')
ipcRenderer.on('asynchronous-reply', (_event, arg) => {console.log(arg) // 在 DevTools 控制台中打印“pong”
})
ipcRenderer.send('asynchronous-message', 'ping')
// 方法3: ipcRenderer.sendSync API 向主进程发送消息,并 同步 等待响应。// 主进程 main.js
const { ipcMain } = require('electron')
ipcMain.on('synchronous-message', (event, arg) => {console.log(arg) // 在 Node 控制台中打印“ping”event.returnValue = 'pong'
})// 预加载脚本 preload.js
const { ipcRenderer } = require('electron')
const result = ipcRenderer.sendSync('synchronous-message', 'ping')
console.log(result) // 在 DevTools 控制台中打印“pong”
双向通讯(渲染进程 <-> 渲染进程)
没有直接的方法可以使用
ipcMain
和ipcRenderer
模块在 Electron 中的渲染器进程之间发送消息。 为此,您有两种选择:
- 将主进程作为渲染器之间的消息代理。 这需要将消息从一个渲染器发送到主进程,然后主进程将消息转发到另一个渲染器。
- 将MessagePort从主进程传递到两个渲染进程。 这将允许在初始设置后渲染器之间直接进行通信。
// 在两个渲染进程之间建立 MessageChannel
// 在这个示例中,主进程设置了一个MessageChannel,然后将每个端口发送给不同的渲染进程。 这样可以让渲染进程彼此之间发送消息,而无需使用主进程作为中转。// 主进程 main.js
const { BrowserWindow, app, MessageChannelMain } = require('electron')app.whenReady().then(async () => {// 创建窗口const mainWindow = new BrowserWindow({show: false,webPreferences: {contextIsolation: false,preload: 'preloadMain.js'}})const secondaryWindow = new BrowserWindow({show: false,webPreferences: {contextIsolation: false,preload: 'preloadSecondary.js'}})// 建立通道const { port1, port2 } = new MessageChannelMain()// webContents准备就绪后,使用postMessage向每个webContents发送一个端口。mainWindow.once('ready-to-show', () => {mainWindow.webContents.postMessage('port', null, [port1])})secondaryWindow.once('ready-to-show', () => {secondaryWindow.webContents.postMessage('port', null, [port2])})
})// 预加载脚本 preload.js
const { ipcRenderer } = require('electron')ipcRenderer.on('port', e => {// 接收到端口,使其全局可用。window.electronMessagePort = e.ports[0]window.electronMessagePort.onmessage = messageEvent => {// 处理消息}
})