第 5 章 · IPC 通信(二):事件推送模式
5.1 为什么需要事件推送
Agent 回复一个长答案时,LLM 是逐 token 流式输出的。如果等全部生成完再 一次性返回,用户要盯着空白界面等十几秒。
体验上正确的做法:主进程每收到一个 chunk,就立刻推给渲染进程,页面边收边渲染。这要求 主进程 → 渲染进程的主动推送:
webContents.send('agent:event', event) // 主进程发
ipcRenderer.on('agent:event', handler) // preload 收,转发给页面
5.2 推送的完整链路
注意关键设计:agent:execute(invoke)和 agent:event(推送)是两条独立通道。invoke 只负责「启动并等待最终结果」,所有中间过程都走事件通道。这是流式任务的标准架构。
5.3 精读:主进程如何发送(electron/ipc/agent.ts)
electron/ipc/agent.ts(节选)
import { ipcMain, BrowserWindow } from 'electron'
function getMainWindow(): BrowserWindow | null {
const windows = BrowserWindow.getAllWindows()
return windows[0] || null
}
// 所有 Agent 事件都经此函数推送
function sendAgentEvent(event: AgentEvent) {
const win = getMainWindow()
if (win && !win.isDestroyed()) {
win.webContents.send('agent:event', event)
}
}
ipcMain.handle('agent:execute', async (_event, params) => {
// ...参数解析后
await startAgentLoop({ ...params, signal: abortController.signal }, sendAgentEvent)
return { success: true }
})
AgentLoop.start 接受一个 onEvent 回调——Loop 本身不知道 Electron 的存在,它只管吐事件;把事件转发到哪个窗口是主进程的 事。又一次看到了 agent-core 与宿主的解耦。
细节:win.isDestroyed() 检查不可省——用户可能在任务执行中关掉窗口。
5.4 精读:preload 的事件订阅(electron/preload.ts)
onAgentEvent: (callback) => {
const handler = (_event: Electron.IpcRendererEvent, data: unknown) => callback(data)
ipcRenderer.on('agent:event', handler)
// 返回取消订阅函数 —— React useEffect cleanup 必需
return () => ipcRenderer.removeListener('agent:event', handler)
}
返回取消订阅函数是 React 世界的关键约定,配合 useEffect 的 cleanup 使用:
useEffect(() => {
const unsubscribe = ipc.onAgentEvent(handleEvent)
return unsubscribe // 组件卸载 / taskId 变化时自动解绑
}, [taskId])
5.5 渲染端封装:src/services/ipc.ts
渲染进程不直接碰 window.electronAPI,而是经过一层统一封装:
src/services/ipc.ts(核心思路)
import type { AgentEvent, ModelConfig } from '../types'
interface ElectronAPI { /* 与 preload 一致的接口 */ }
declare global {
interface Window {
electronAPI?: ElectronAPI
}
}
function detectEnv(): 'electron' | 'browser' {
return typeof window !== 'undefined' && window.electronAPI
? 'electron'
: 'browser'
}
这层封装做三件事:
- 类型收窄:把
unknown事件转成判别联合类型AgentEvent - 环境探测:
window.electronAPI存在 → Electron;否则 → 浏览器 - Mock 分流:浏览器环境下自动切换到 Mock 实现
5.6 浏览器 Mock 模式
pnpm dev