-
Notifications
You must be signed in to change notification settings - Fork 2
perf: 会话切换及大量消息渲染仍存在卡顿 #4
Copy link
Copy link
Open
Labels
Description
问题描述
完成 Rust 后端全量计算迁移 + 前端渐进式渲染优化后,大部分场景已流畅,但以下场景仍存在 1-2 秒 可感知的卡顿:
- 会话切换:从一个大会话切换到另一个大会话时,有明显延迟
- 大量消息渲染:500+ 条消息的会话在 idle 扩散完成前,滚动到未渲染区域有短暂空白
当前架构
Rust 后端(已优化,不是瓶颈)
- rayon 并行分类/转换消息
- memchr SIMD 加速搜索
- 内存缓存 TransformedSession
前端渐进式渲染(当前瓶颈所在)
useProgressiveRenderhook:ref + version 计数器模式- 初始渲染 80 条 → idle 扩散(每批 60 条,每 250 条 flush 一次 React 重渲染)
- 滚动 handler 使用 rAF 节流 + 二分查找视口中心
已完成的优化
| 提交 | 内容 |
|---|---|
2b836ca |
Rust 后端全量计算迁移 + 渐进式渲染 hook |
f8f8962 |
移除 content-visibility: auto(与渐进式渲染冲突导致 scrollHeight 不稳定) |
4fbfa0d |
移除消息级 motion.div、rAF 节流 handleScroll、加大批次参数 |
剩余瓶颈分析
1. 会话切换时的完整重建开销
切换会话时,整个消息列表被销毁并重建:
- React 需要卸载旧消息的所有 DOM 节点
- 然后创建新会话的全部节点(80 条初始 + 占位符)
visibleMessages从数组 A 切换为数组 B,key 全部不同,无法复用
可能的优化方向:
- 会话级缓存:缓存已渲染会话的 DOM 快照或 React 状态,切换时恢复而非重建
startTransition/useDeferredValue:将消息列表更新标记为低优先级过渡
2. 消息组件未 memoize
每次 version bump 触发 ChatView 重渲染时,visibleMessages.map() 对所有消息重新求值:
- 已渲染的消息虽然 VDOM diff 后不产生 DOM 变更,但 JSX 创建 + diff 本身有成本
- MessageBlockList / MessageContentRenderer 等子组件未用 React.memo
可能的优化方向:
- 将消息渲染抽取为
React.memo组件,props 为(msg, isRendered: boolean, ...) - 将回调函数用
useCallback稳定化,避免 memo 失效
3. Markdown 渲染开销
每条消息的 Markdown 内容(代码块、表格等)在首次渲染时需要解析 + 生成 React 元素树。500 条消息的 Markdown 累积解析时间可能达数百毫秒。
可能的优化方向:
- Rust 端预渲染 Markdown 为 HTML 字符串,前端直接
dangerouslySetInnerHTML - 前端 Markdown 组件 memoize(按 text 内容 hash)
4. requestIdleCallback 的局限性
requestIdleCallback 在浏览器繁忙时可能长时间不调度,导致扩散进度停滞。
可能的优化方向:
- 混合策略:idle 优先,但超时后降级为
setTimeout(0)保底
关键文件
src/hooks/useProgressiveRender.ts— 渐进式渲染 hooksrc/components/ChatView.tsx— 消息列表渲染主体src/components/MessageBlockList.tsx— 消息内容块渲染src-tauri/src/services/transformer.rs— Rust 消息转换器
环境
- Tauri v2 + React 19 + TypeScript
- 测试设备:Windows 11
- 典型会话大小:200-800 条消息
Reactions are currently unavailable