Electron IPC Main API 知识点总结
目录
- [1. IPC 通信概述](#1. IPC 通信概述 "#1-ipc-%E9%80%9A%E4%BF%A1%E6%A6%82%E8%BF%B0")
- [2. IPC 通信架构](#2. IPC 通信架构 "#2-ipc-%E9%80%9A%E4%BF%A1%E6%9E%B6%E6%9E%84")
- [3. 监听方法](#3. 监听方法 "#3-%E7%9B%91%E5%90%AC%E6%96%B9%E6%B3%95")
- [4. 消息发送模式](#4. 消息发送模式 "#4-%E6%B6%88%E6%81%AF%E5%8F%91%E9%80%81%E6%A8%A1%E5%BC%8F")
- [5. invoke/handle 模式(推荐)](#5. invoke/handle 模式(推荐) "#5-invokehandle-%E6%A8%A1%E5%BC%8F%E6%8E%A8%E8%8D%90")
- [6. IpcMainEvent 对象](#6. IpcMainEvent 对象 "#6-ipcmainevent-%E5%AF%B9%E8%B1%A1")
- [7. 最佳实践](#7. 最佳实践 "#7-%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5")
1. IPC 通信概述
什么是 IPC?
IPC(Inter-Process Communication) 是 Electron 中主进程 与渲染进程之间通信的机制。
javascript
┌─────────────────────────────────────────────────────┐
│ 主进程 (Main) │
│ │
│ const { ipcMain } = require('electron') │
│ │
│ ipcMain.handle('channel', handler) │
│ ↓ 接收请求 │
│ ↓ 返回结果 │
└─────────────────────┬───────────────────────────────┘
│ IPC
┌─────────────────────↓───────────────────────────────┐
│ 渲染进程 (Renderer) │
│ │
│ const { ipcRenderer } = require('electron') │
│ │
│ ipcRenderer.invoke('channel', ...args) │
└─────────────────────────────────────────────────────┘
2. IPC 通信架构
通信方向
| 方向 | 方法 | 说明 |
|---|---|---|
| 渲染 → 主 | ipcRenderer.send() |
异步发送 |
| 渲染 → 主 | ipcRenderer.invoke() |
异步等待响应 |
| 主 → 渲染 | webContents.send() |
主进程主动推送 |
| 渲染 → 主 → 渲染 | event.reply() |
回复发送者 |
3. 监听方法
3.1 基础监听 ipcMain.on()
javascript
const { ipcMain } = require('electron')
ipcMain.on('message', (event, arg1, arg2) => {
console.log('收到消息:', arg1, arg2)
})
3.2 一次性监听 ipcMain.once()
javascript
ipcMain.once('single-use', (event, data) => {
console.log('只会触发一次')
})
3.3 移除监听
javascript
// 移除特定监听器
ipcMain.off('channel', handlerFunction)
// 移除 channel 的所有监听器
ipcMain.removeAllListeners('channel')
// 移除所有监听器
ipcMain.removeAllListeners()
4. 消息发送模式
4.1 同步消息(不推荐)
javascript
// 主进程
ipcMain.on('sync-message', (event) => {
event.returnValue = '同步响应' // 直接返回值
})
javascript
// 渲染进程
const result = ipcRenderer.sendSync('sync-message')
console.log(result) // '同步响应'
⚠️ 注意:同步消息会阻塞渲染进程,建议使用 invoke/handle 模式
4.2 异步消息 + 回复
javascript
// 主进程
ipcMain.on('async-message', (event, data) => {
console.log('收到:', data)
// 回复发送者
event.reply('response', '处理完成')
})
javascript
// 渲染进程
ipcRenderer.send('async-message', { name: 'test' })
ipcRenderer.on('response', (event, data) => {
console.log('收到回复:', data)
})
4.3 从子窗口/iframe 发送
event.reply() 会自动处理从非主 frame 发送的消息:
javascript
// 主进程
ipcMain.on('from-iframe', (event) => {
// event.sender 就是 WebContents
// 可以发送到主 frame
event.sender.send('to-main-frame', data)
})
5. invoke/handle 模式(推荐)⭐
这是最推荐的通信方式,支持 Promise。
主进程:定义处理程序
javascript
const { ipcMain } = require('electron')
ipcMain.handle('get-user-data', async (event, userId) => {
// event: IpcMainInvokeEvent
console.log('请求来自:', event.senderFrame.origin)
const userData = await fetchUserFromDatabase(userId)
return userData // 返回值会作为 Promise 的结果
})
渲染进程:调用
javascript
const { ipcRenderer } = require('electron')
async function loadUser(userId) {
try {
const user = await ipcRenderer.invoke('get-user-data', userId)
console.log('用户数据:', user)
} catch (error) {
console.error('获取失败:', error.message)
}
}
单次 handle
javascript
// 只处理一次,然后自动移除
ipcMain.handleOnce('single-invoke', async (event) => {
return { once: true }
})
移除 handle
javascript
ipcMain.removeHandler('get-user-data')
错误处理
javascript
// 主进程
ipcMain.handle('might-fail', async (event, data) => {
try {
const result = await riskyOperation(data)
return result
} catch (error) {
throw new Error('操作失败: ' + error.message)
}
})
// 渲染进程
const result = await ipcRenderer.invoke('might-fail', data)
// 如果主进程抛出异常,这里会 reject
6. IpcMainEvent 对象
监听器接收的 event 对象包含:
| 属性 | 说明 |
|---|---|
event.sender |
发送消息的 WebContents |
event.senderFrame |
发送消息的 Frame |
event.reply() |
回复消息给发送者 |
event.returnValue |
设置同步消息的返回值 |
event.preventDefault() |
阻止默认行为 |
使用示例
javascript
ipcMain.on('some-event', (event, ...args) => {
// 回复消息
event.reply('response-channel', { status: 'ok' })
// 获取发送者信息
const frame = event.senderFrame
console.log('来源:', frame?.origin)
// 主动向发送者推送消息
event.sender.send('push-message', '来自主进程')
})
7. 最佳实践
7.1 推荐:使用 invoke/handle
javascript
// ✅ 推荐:清晰的 Promise 风格
ipcMain.handle('fetch-data', async (event, id) => {
return await database.query(id)
})
// ❌ 避免:同步阻塞
ipcMain.on('sync-fetch', (event) => {
event.returnValue = database.querySync(id)
})
7.2 封装 IPC 调用
javascript
// preload.js - 安全暴露 API
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
// 单向通知
send: (channel, data) => ipcRenderer.send(channel, data),
// 监听消息
on: (channel, callback) => {
ipcRenderer.on(channel, (event, ...args) => callback(...args))
},
// 双向通信(推荐)
invoke: (channel, ...args) => ipcRenderer.invoke(channel, ...args)
})
javascript
// 渲染进程中使用
// 安全的双向通信
const userData = await window.electronAPI.invoke('get-user', userId)
// 发送通知
window.electronAPI.send('user-logged-in', { userId })
7.3 命名规范
javascript
// 推荐:使用命名空间格式
'window:minimize'
'window:maximize'
'file:open'
'file:save'
'settings:get'
'settings:set'
// 避免:随意命名
'message'
'data'
'get'
7.4 错误处理
javascript
// 主进程
ipcMain.handle('risky-operation', async (event, data) => {
if (!data) {
throw new Error('数据不能为空')
}
// ... 业务逻辑
})
// 渲染进程
try {
const result = await ipcRenderer.invoke('risky-operation', data)
} catch (error) {
console.error('操作失败:', error.message)
}
7.5 清理监听器
javascript
// 组件卸载时清理
onUnmounted(() => {
ipcRenderer.removeAllListeners('channel-name')
})
// 或使用 once 避免手动清理
ipcRenderer.once('one-time-event', (event, data) => {
// 处理后自动移除
})
完整示例
主进程 (main.js)
javascript
const { app, BrowserWindow, ipcMain } = require('electron')
let mainWindow
app.whenReady().then(() => {
mainWindow = new BrowserWindow()
// 处理获取数据请求
ipcMain.handle('get-config', async (event, key) => {
return { theme: 'dark', language: 'zh-CN' }
})
// 处理保存数据
ipcMain.handle('save-config', async (event, config) => {
await saveToFile(config)
return { success: true }
})
// 监听窗口控制
ipcMain.on('window-minimize', () => {
mainWindow.minimize()
})
ipcMain.on('window-maximize', () => {
if (mainWindow.isMaximized()) {
mainWindow.unmaximize()
} else {
mainWindow.maximize()
}
})
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})
预加载脚本 (preload.js)
javascript
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
getConfig: (key) => ipcRenderer.invoke('get-config', key),
saveConfig: (config) => ipcRenderer.invoke('save-config', config),
minimize: () => ipcRenderer.send('window-minimize'),
maximize: () => ipcRenderer.send('window-maximize'),
onUpdate: (callback) => ipcRenderer.on('config-updated', callback)
})
渲染进程 (renderer.js)
javascript
// 获取配置
const config = await window.electronAPI.getConfig('theme')
// 保存配置
await window.electronAPI.saveConfig({ theme: 'light' })
// 窗口控制
window.electronAPI.minimize()
window.electronAPI.maximize()
// 监听更新
window.electronAPI.onUpdate((newConfig) => {
console.log('配置已更新:', newConfig)
})
方法速查表
| 方法 | 说明 | 返回值 |
|---|---|---|
ipcMain.on(channel, fn) |
持续监听 | - |
ipcMain.once(channel, fn) |
单次监听 | - |
ipcMain.off(channel, fn) |
移除监听 | - |
ipcMain.handle(channel, fn) |
处理 Invoke 请求(推荐) | Promise |
ipcMain.handleOnce(channel, fn) |
单次处理 Invoke | Promise |
ipcMain.removeHandler(channel) |
移除处理程序 | - |
ipcMain.removeAllListeners(channel) |
移除所有监听器 | - |
文档基于 Electron v28+ IPC Main API 编写