主题
IPC 通信
IPC 用于主进程和渲染进程通信。推荐把 IPC 当作本地 RPC 使用:每个 channel 都代表一个明确能力。
请求响应
主进程注册 handler:
ts
import { ipcMain } from 'electron'
ipcMain.handle('app:version', () => {
return process.env.npm_package_version
})Preload 暴露 API:
ts
import { contextBridge, ipcRenderer } from 'electron'
contextBridge.exposeInMainWorld('appApi', {
getVersion: () => ipcRenderer.invoke('app:version'),
})渲染进程调用:
ts
const version = await window.appApi.getVersion()事件推送
主进程向窗口推送事件:
ts
win.webContents.send('download:progress', { percent: 50 })Preload 包装监听能力:
ts
import { contextBridge, ipcRenderer } from 'electron'
contextBridge.exposeInMainWorld('downloadApi', {
onProgress: (callback: (payload: { percent: number }) => void) => {
const listener = (_event: Electron.IpcRendererEvent, payload: { percent: number }) => {
callback(payload)
}
ipcRenderer.on('download:progress', listener)
return () => {
ipcRenderer.removeListener('download:progress', listener)
}
},
})渲染进程使用:
ts
const off = window.downloadApi.onProgress((payload) => {
console.log(payload.percent)
})
off()Channel 命名
推荐使用命名空间:
bash
app:version
settings:get
settings:set
file:open
download:progress避免使用:
bash
message
event
invoke
executechannel 名称越泛,越容易演变成万能通道。
参数校验
不要信任渲染进程传入的参数。
ts
ipcMain.handle('settings:set', (_event, key: unknown, value: unknown) => {
if (typeof key !== 'string' || key.length === 0) {
throw new Error('Invalid key')
}
if (typeof value !== 'string') {
throw new Error('Invalid value')
}
return settingsService.set(key, value)
})如果参数结构复杂,可以使用 zod、valibot 等 schema 校验库。
封装建议
- 不在渲染进程直接使用
ipcRenderer。 - 不暴露通用
send(channel, ...args)。 - 一个业务能力对应一个明确 API。
- handler 内只做参数校验和服务调用,复杂逻辑放到 service。
- 渲染进程拿到的是普通数据,不是主进程对象引用。
