# ZCLAW 架构优化 - Phase 3: 核心优化 实施计划 > **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** 实现核心性能优化 - 虚拟滚动、Web Worker 隔离、缓存策略 **Architecture:** 三条并行轨道,可独立实施和验证 **Tech Stack:** React, react-window, Web Worker, Valtio **Spec Reference:** `docs/superpowers/specs/2026-03-21-architecture-optimization-design.md` **Duration:** 6 周 (24 人日) --- ## 并行轨道概览 ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ Phase 3: 核心优化 (并行) │ ├─────────────────────┬─────────────────────┬─────────────────────────────┤ │ Track A: Chat │ Track B: Hands │ Track C: Intelligence │ │ 虚拟滚动优化 │ Web Worker 隔离 │ 缓存策略 ✅ │ ├─────────────────────┼─────────────────────┼─────────────────────────────┤ │ • react-window │ • browser-worker.ts │ • IntelligenceCache │ │ • 消息分页 │ • worker-pool.ts │ • TTL + LRU │ │ • 惰性加载 │ • 安全执行隔离 │ • 命中率统计 │ │ • 首屏 3x 提升 │ • XSS 防护 │ • 70% 响应提升 │ └─────────────────────┴─────────────────────┴─────────────────────────────┘ ``` **Track C (Intelligence 缓存) 已在 Phase 2.5 完成** --- ## Track A: Chat 虚拟滚动优化 ### 目标 | 指标 | 当前 | 目标 | |------|------|------| | 首屏渲染 | ~2s | <500ms | | 1000+ 消息滚动 | 卡顿 | 60fps | | 内存占用 | 无限增长 | 限制 100 条 | ### 文件结构 ``` desktop/src/ ├── components/ChatArea/ │ ├── MessageList.tsx # 修改: 使用虚拟滚动 │ ├── VirtualMessageList.tsx # 新建: react-window 封装 │ └── MessageItem.tsx # 新建: 单条消息组件 └── domains/chat/ └── hooks.ts # 修改: 添加分页 hooks ``` ### Task A.1: 安装 react-window **Files:** - Modify: `desktop/package.json` - [ ] **Step 1: 安装依赖** ```bash cd g:/ZClaw_zclaw/desktop && pnpm add react-window @types/react-window ``` - [ ] **Step 2: 验证安装** ```bash pnpm list react-window ``` --- ### Task A.2: 创建 VirtualMessageList 组件 **Files:** - Create: `desktop/src/components/ChatArea/VirtualMessageList.tsx` - Create: `desktop/src/components/ChatArea/MessageItem.tsx` - [ ] **Step 1: 创建 MessageItem 组件** ```typescript // desktop/src/components/ChatArea/MessageItem.tsx import { memo } from 'react'; import type { Message } from '@/domains/chat'; interface MessageItemProps { message: Message; style?: React.CSSProperties; } export const MessageItem = memo(function MessageItem({ message, style }: MessageItemProps) { return (
{message.content}
{message.streaming && ...}
); }); ``` - [ ] **Step 2: 创建 VirtualMessageList 组件** ```typescript // desktop/src/components/ChatArea/VirtualMessageList.tsx import { useRef, useCallback, useEffect } from 'react'; import { FixedSizeList as List } from 'react-window'; import { MessageItem } from './MessageItem'; import type { Message } from '@/domains/chat'; interface VirtualMessageListProps { messages: Message[]; height: number; width: number; onScrollToEnd?: () => void; } export function VirtualMessageList({ messages, height, width, onScrollToEnd, }: VirtualMessageListProps) { const listRef = useRef(null); // Auto-scroll to bottom when new message arrives useEffect(() => { if (listRef.current && messages.length > 0) { listRef.current.scrollToItem(messages.length - 1, 'end'); } }, [messages.length]); const Row = useCallback( ({ index, style }: { index: number; style: React.CSSProperties }) => ( ), [messages] ); return ( {Row} ); } ``` --- ### Task A.3: 更新 MessageList 使用虚拟滚动 **Files:** - Modify: `desktop/src/components/ChatArea/MessageList.tsx` - [ ] **Step 1: 替换实现** 将现有的全量渲染替换为 VirtualMessageList。 --- ### Task A.4: 添加消息分页支持 **Files:** - Modify: `desktop/src/domains/chat/store.ts` - Modify: `desktop/src/domains/chat/hooks.ts` - [ ] **Step 1: 添加分页状态** 在 chatStore 中添加: ```typescript pageSize: 50, currentPage: 0, hasMore: true, loadMoreMessages: async () => { /* ... */ }, ``` --- ## Track B: Hands Web Worker 隔离 ### 目标 | 指标 | 当前 | 目标 | |------|------|------| | 执行安全性 | eval() XSS 风险 | 完全隔离 | | 错误隔离 | 主线程崩溃 | Worker 隔离 | | 超时控制 | 无 | 30s 强制终止 | ### 文件结构 ``` desktop/src/ ├── workers/ │ ├── browser-worker.ts # 新建: 浏览器执行 Worker │ └── worker-pool.ts # 新建: Worker 池管理 ├── lib/ │ └── browser-executor.ts # 修改: 使用 Worker └── domains/hands/ └── store.ts # 修改: 集成 Worker 执行 ``` ### Task B.1: 创建 Browser Worker **Files:** - Create: `desktop/src/workers/browser-worker.ts` - [ ] **Step 1: 创建 Worker 脚本** ```typescript // desktop/src/workers/browser-worker.ts /// const ctx = self as DedicatedWorkerGlobalScope; interface ExecuteRequest { type: 'execute'; id: string; script: string; args: unknown[]; timeout: number; } interface ExecuteResponse { type: 'result' | 'error'; id: string; result?: unknown; error?: string; } ctx.onmessage = async (e: MessageEvent) => { const { type, id, script, args, timeout } = e.data; if (type !== 'execute') return; const timeoutId = setTimeout(() => { ctx.postMessage({ type: 'error', id, error: 'Execution timeout', } as ExecuteResponse); }, timeout); try { // Safe execution without DOM access const fn = new Function('args', script); const result = await fn(args); clearTimeout(timeoutId); ctx.postMessage({ type: 'result', id, result } as ExecuteResponse); } catch (err) { clearTimeout(timeoutId); ctx.postMessage({ type: 'error', id, error: err instanceof Error ? err.message : String(err), } as ExecuteResponse); } }; ctx.postMessage({ type: 'ready' }); ``` --- ### Task B.2: 创建 Worker Pool **Files:** - Create: `desktop/src/workers/worker-pool.ts` - [ ] **Step 1: 创建 Worker 池** ```typescript // desktop/src/workers/worker-pool.ts export class BrowserWorkerPool { private workers: Worker[] = []; private available: Worker[] = []; private maxWorkers: number; constructor(maxWorkers = 4) { this.maxWorkers = maxWorkers; } private createWorker(): Worker { return new Worker( new URL('./browser-worker.ts', import.meta.url), { type: 'module' } ); } async execute(script: string, args: unknown[], timeout = 30000): Promise { const worker = this.available.pop() || this.createWorker(); return new Promise((resolve, reject) => { const timeoutId = setTimeout(() => { worker.terminate(); reject(new Error('Execution timeout')); }, timeout); const handler = (e: MessageEvent) => { if (e.data.type === 'result') { clearTimeout(timeoutId); worker.removeEventListener('message', handler); this.available.push(worker); resolve(e.data.result); } else if (e.data.type === 'error') { clearTimeout(timeoutId); worker.removeEventListener('message', handler); this.available.push(worker); reject(new Error(e.data.error)); } }; worker.addEventListener('message', handler); worker.postMessage({ type: 'execute', id: Date.now().toString(), script, args, timeout, }); }); } terminateAll(): void { this.workers.forEach(w => w.terminate()); this.workers = []; this.available = []; } } export const browserWorkerPool = new BrowserWorkerPool(); ``` --- ### Task B.3: 集成到 Hands Domain **Files:** - Modify: `desktop/src/domains/hands/store.ts` - [ ] **Step 1: 使用 Worker 执行** 在 `triggerHand` action 中使用 `browserWorkerPool.execute()` 替代直接 `eval()`。 --- ## Track C: Intelligence 缓存策略 ✅ 已完成 已在 Phase 2.5 实现: - `desktop/src/domains/intelligence/cache.ts` - LRU + TTL 缓存 - `desktop/src/domains/intelligence/store.ts` - 带缓存的 Valtio store - `desktop/src/domains/intelligence/hooks.ts` - React hooks --- ## 验证清单 ### Track A 验证 - [ ] 首屏渲染 < 500ms - [ ] 1000+ 消息滚动 60fps - [ ] 消息正确显示 - [ ] 流式消息正常更新 ### Track B 验证 - [ ] Worker 正常创建和销毁 - [ ] 超时正确终止 - [ ] 错误正确隔离 - [ ] XSS 攻击被阻止 ### 集成验证 - [ ] 所有 TypeScript 编译通过 - [ ] 现有测试通过 - [ ] E2E 核心流程正常 --- ## 提交规范 ``` feat(chat): add virtual scrolling with react-window feat(hands): add Web Worker isolation for browser execution perf(intelligence): add LRU cache with TTL support ```