概述
Electron 应用由主进程和渲染进程组成,它们运行在不同的进程中,需要通过 IPC (Inter-Process Communication) 进行通信。Electron 提供了三种主要的通信方式:
- sendSync & returnValue - 同步通信
- send & reply - 异步通信
- invoke & handle - 现代异步通信(推荐)
1. sendSync & returnValue - 同步通信
使用场景
- 需要立即获取返回值的简单操作
- 数据量小且处理时间短的场景
实现方式
主进程 (main/index.js):
const { ipcMain } = require("electron")
// 监听同步消息
ipcMain.on("sync-message", (event, data) => {
console.log("收到同步消息:", data)
// 处理数据
const result = processData(data)
// 返回结果
event.returnValue = result
})
渲染进程 (preload/index.js):
const { ipcRenderer } = require("electron")
// 发送同步消息并获取返回值
const result = ipcRenderer.sendSync("sync-message", { id: 1, name: "test" })
console.log("同步返回结果:", result)
注意事项
- ⚠️ 会阻塞渲染进程,可能导致界面卡顿
- 仅适用于轻量级操作
- 不推荐用于复杂或耗时的操作
2. send & reply - 异步通信
使用场景
- 需要异步处理的复杂操作
- 需要保持界面响应性的场景
实现方式
主进程 (main/index.js):
const { ipcMain } = require("electron")
// 监听异步消息
ipcMain.on("async-message", (event, data) => {
console.log("收到异步消息:", data)
// 异步处理数据
processDataAsync(data)
.then((result) => {
// 回复结果
event.reply("async-reply", { success: true, data: result })
})
.catch((error) => {
event.reply("async-reply", { success: false, error: error.message })
})
})
渲染进程 (preload/index.js):
const { ipcRenderer } = require("electron")
// 发送异步消息
ipcRenderer.send("async-message", { id: 1, name: "test" })
// 监听回复
ipcRenderer.on("async-reply", (event, result) => {
if (result.success) {
console.log("异步处理成功:", result.data)
} else {
console.error("异步处理失败:", result.error)
}
})
注意事项
- ✅ 不会阻塞渲染进程
- 需要手动管理事件监听器
- 可能出现内存泄漏风险
3. invoke & handle - 现代异步通信(推荐)
使用场景
- 所有异步通信场景
- 需要 Promise 支持的现代开发
实现方式
主进程 (main/index.js):
const { ipcMain } = require("electron")
// 注册处理器
ipcMain.handle("get-user-data", async (event, userId) => {
try {
const userData = await fetchUserData(userId)
return { success: true, data: userData }
} catch (error) {
return { success: false, error: error.message }
}
})
ipcMain.handle("save-user-data", async (event, userData) => {
try {
await saveUserData(userData)
return { success: true }
} catch (error) {
return { success: false, error: error.message }
}
})
预加载脚本 (preload/index.js):
const { contextBridge, ipcRenderer } = require("electron")
// 暴露安全的 API 到渲染进程
contextBridge.exposeInMainWorld("electronAPI", {
// 用户数据相关
user: {
getUserData: (userId) => ipcRenderer.invoke("get-user-data", userId),
saveUserData: (userData) => ipcRenderer.invoke("save-user-data", userData),
},
// 主题相关
theme: {
isDarkMode: () => ipcRenderer.invoke("isDarkMode"),
setTheme: (theme) => ipcRenderer.invoke("setTheme", theme),
},
// 窗口控制
window: {
minimize: () => ipcRenderer.invoke("window-minimize"),
maximize: () => ipcRenderer.invoke("window-maximize"),
close: () => ipcRenderer.invoke("window-close"),
},
})
渲染进程 (renderer.js):
// 使用 async/await 调用
async function loadUserData(userId) {
try {
const result = await window.electronAPI.user.getUserData(userId)
if (result.success) {
console.log("用户数据:", result.data)
return result.data
} else {
console.error("获取用户数据失败:", result.error)
}
} catch (error) {
console.error("调用失败:", error)
}
}
// 使用 Promise 调用
function saveUserData(userData) {
window.electronAPI.user
.saveUserData(userData)
.then((result) => {
if (result.success) {
console.log("保存成功")
} else {
console.error("保存失败:", result.error)
}
})
.catch((error) => {
console.error("调用失败:", error)
})
}
最佳实践建议
1. 安全性优先
// ✅ 推荐:使用 contextIsolation 和 preload 脚本
webPreferences: {
contextIsolation: true,
nodeIntegration: false,
preload: path.join(__dirname, 'preload.js')
}
// ❌ 避免:直接暴露 ipcRenderer
// window.ipcRenderer = require('electron').ipcRenderer
2. 错误处理
// 主进程
ipcMain.handle("api-call", async (event, data) => {
try {
const result = await processData(data)
return { success: true, data: result }
} catch (error) {
console.error("API 调用失败:", error)
return { success: false, error: error.message }
}
})
// 渲染进程
async function callAPI(data) {
try {
const result = await window.electronAPI.someMethod(data)
if (result.success) {
return result.data
} else {
throw new Error(result.error)
}
} catch (error) {
console.error("调用失败:", error)
// 显示用户友好的错误信息
showErrorMessage(error.message)
}
}
3. 类型安全(TypeScript)
// 定义 API 接口
interface ElectronAPI {
user: {
getUserData: (userId: string) => Promise<ApiResponse<UserData>>
saveUserData: (userData: UserData) => Promise<ApiResponse<void>>
}
theme: {
isDarkMode: () => Promise<boolean>
setTheme: (theme: "light" | "dark" | "system") => Promise<boolean>
}
}
declare global {
interface Window {
electronAPI: ElectronAPI
}
}
4. 性能优化
// 避免频繁的 IPC 调用
let cachedTheme = null
async function getTheme() {
if (cachedTheme === null) {
cachedTheme = await window.electronAPI.theme.isDarkMode()
}
return cachedTheme
}
// 批量处理
async function batchProcess(items) {
const promises = items.map((item) => window.electronAPI.processItem(item))
return Promise.all(promises)
}
5. 事件清理
// 在组件卸载时清理事件监听器
class MyComponent {
constructor() {
this.handleThemeChange = this.handleThemeChange.bind(this)
window.electronAPI.theme.onThemeChange(this.handleThemeChange)
}
destroy() {
// 清理事件监听器
window.electronAPI.theme.offThemeChange(this.handleThemeChange)
}
}
总结
通信方式 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
sendSync & returnValue | 轻量级同步操作 | 简单直接 | 阻塞渲染进程 |
send & reply | 复杂异步操作 | 不阻塞界面 | 需要手动管理事件 |
invoke & handle | 现代异步通信 | Promise 支持,类型安全 | 需要预加载脚本 |
推荐使用顺序:
- invoke & handle - 首选,现代且安全
- send & reply - 复杂场景的备选方案
- sendSync & returnValue - 仅在必要时使用
通过遵循这些最佳实践,你可以构建出安全、高效且易于维护的 Electron 应用。