ipcRenderer
ipcRenderer 是 Electron 渲染进程中用于与主进程通信的模块。
注意
在现代 Electron 应用中,推荐通过 preload 脚本和 contextBridge 暴露安全的 IPC 接口,而不是直接在渲染进程中使用 ipcRenderer。
基本用法
发送消息
javascript
const { ipcRenderer } = require('electron')
// 发送异步消息(不等待返回)
ipcRenderer.send('channel-name', data)
// 发送同步消息(阻塞等待返回)
const result = ipcRenderer.sendSync('sync-channel', data)
// 发送消息并等待 Promise 返回
const response = await ipcRenderer.invoke('async-channel', data)接收消息
javascript
// 监听主进程消息
ipcRenderer.on('channel-name', (event, data) => {
console.log('Received:', data)
})
// 只监听一次
ipcRenderer.once('channel-name', (event, data) => {
console.log('Received once:', data)
})主要方法
send(channel, ...args)
向主进程发送异步消息:
javascript
// 渲染进程
ipcRenderer.send('window:minimize')
ipcRenderer.send('file:save', { path: '/path/to/file', content: 'Hello' })
// 主进程
ipcMain.on('window:minimize', (event) => {
BrowserWindow.fromWebContents(event.sender)?.minimize()
})
ipcMain.on('file:save', (event, { path, content }) => {
fs.writeFileSync(path, content)
// 发送回执
event.reply('file:saved', { success: true })
})invoke(channel, ...args)
发送消息并返回 Promise(推荐使用):
javascript
// 渲染进程
const files = await ipcRenderer.invoke('dialog:open', {
filters: [{ name: 'Images', extensions: ['png', 'jpg'] }]
})
// 主进程
ipcMain.handle('dialog:open', async (event, options) => {
const result = await dialog.showOpenDialog(options)
return result.filePaths
})sendSync(channel, ...args)
发送同步消息(不推荐,会阻塞渲染进程):
javascript
// 渲染进程
const isMaximized = ipcRenderer.sendSync('window:isMaximized')
// 主进程
ipcMain.on('window:isMaximized', (event) => {
event.returnValue = BrowserWindow.fromWebContents(event.sender)?.isMaximized()
})on(channel, listener)
监听主进程发送的消息:
javascript
ipcRenderer.on('update:progress', (event, progress) => {
console.log(`Download progress: ${progress}%`)
})once(channel, listener)
只监听一次:
javascript
ipcRenderer.once('app:ready', () => {
console.log('App is ready')
})removeListener(channel, listener)
移除监听器:
javascript
const handler = (event, data) => console.log(data)
ipcRenderer.on('channel', handler)
// 移除
ipcRenderer.removeListener('channel', handler)removeAllListeners(channel)
移除所有监听器:
javascript
ipcRenderer.removeAllListeners('channel')通过 Preload 使用(推荐)
javascript
// preload.js
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
// invoke 方法
openDialog: (options) => ipcRenderer.invoke('dialog:open', options),
// send 方法
minimizeWindow: () => ipcRenderer.send('window:minimize'),
// on 方法(返回取消监听函数)
onUpdateProgress: (callback) => {
const handler = (event, progress) => callback(progress)
ipcRenderer.on('update:progress', handler)
return () => ipcRenderer.removeListener('update:progress', handler)
}
})javascript
// renderer.js
// 使用暴露的 API
const files = await window.electronAPI.openDialog({ properties: ['openFile'] })
window.electronAPI.minimizeWindow()
const unsubscribe = window.electronAPI.onUpdateProgress((progress) => {
console.log(progress)
})
// 取消监听
unsubscribe()事件对象
IPC 回调中的 event 对象包含以下属性:
javascript
ipcRenderer.on('channel', (event, data) => {
event.senderId // 发送者的 ID
event.sender // 发送消息的 WebContents
event.ports // MessagePort 数组
// 发送回执
event.reply('reply-channel', responseData)
// 返回值(用于 sendSync)
event.returnValue = result
})MessageChannel 通信
使用 MessageChannel 进行双向通信:
javascript
// 渲染进程
const { port1, port2 } = new MessageChannel()
// 发送一个端口给主进程
ipcRenderer.postMessage('port', null, [port1])
// 使用另一个端口接收消息
port2.onmessage = (event) => {
console.log('Received:', event.data)
}
// 发送消息
port2.postMessage('Hello from renderer')javascript
// 主进程
ipcMain.on('port', (event) => {
const [port] = event.ports
port.onmessage = (e) => {
console.log('Received:', e.data)
port.postMessage('Hello from main')
}
port.start()
})完整示例
javascript
// preload.js
const { contextBridge, ipcRenderer } = require('electron')
const ALLOWED_CHANNELS = {
invoke: ['dialog:open', 'file:read', 'file:write'],
send: ['window:minimize', 'window:maximize', 'window:close'],
on: ['file:changed', 'update:available']
}
contextBridge.exposeInMainWorld('ipc', {
invoke: (channel, ...args) => {
if (!ALLOWED_CHANNELS.invoke.includes(channel)) {
return Promise.reject(new Error(`Invalid channel: ${channel}`))
}
return ipcRenderer.invoke(channel, ...args)
},
send: (channel, ...args) => {
if (!ALLOWED_CHANNELS.send.includes(channel)) {
throw new Error(`Invalid channel: ${channel}`)
}
ipcRenderer.send(channel, ...args)
},
on: (channel, callback) => {
if (!ALLOWED_CHANNELS.on.includes(channel)) {
throw new Error(`Invalid channel: ${channel}`)
}
const handler = (event, ...args) => callback(...args)
ipcRenderer.on(channel, handler)
return () => ipcRenderer.removeListener(channel, handler)
}
})