mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-28 13:23:03 +00:00
用户报「聊天页面卡得让人没有用的欲望」+「打开 app 和 webui,没有停在
最新消息处,还要反复滑动」。真机实测(Xvfb + Electron + CDP)定位到两个
根因,都修了。
## 根因 1:renderChat 每次全量重建 innerHTML
200 条消息 = 3500 个 DOM 节点全部销毁重建。拆分测量:
200 条:整体 234.6ms,其中 renderMd 25.4ms(**11%**)
⇒ markdown 渲染只占 11%,**89% 在 DOM 写入与布局**。
流式追加时每个放行的 chunk 都走这条路(200 条时每 chunk 6.6ms),
聊到几百条就是 0.5 秒/次 —— 这就是体感。
**改法**:按 `data-msgkey` 复用节点,四种策略按代价从低到高:
尾部追加(最常见)→ 头部前插(loadOlderChat)→ 局部替换 → 兜底整棵重建。
`data-msgkey` = role + 序号 + 内容长度 + 首尾片段。
★ **不能靠下标定位**:`loadOlderChat` 会 `unshift` 前插消息,下标整体位移。
实测:
| 消息数 | 改前 | 改后 | 改善 |
| --- | --- | --- | --- |
| 50 | 59ms | **8.2ms** | 7.2× |
| 200 | 235ms | **23.5ms** | 10× |
| 400 | 474ms | **38.2ms** | 12× |
关键是**次线性**了:400 条只比 200 条多 15ms(改前多 240ms)。
## 根因 2:滚动没落地
scrollTo 被调: 1, 参数: {top: 23446, behavior: "smooth"}
scrollTop: 0 ← 调了,但没生效
可滚动上限: 22838
smooth 立即值 0、300ms 后只到 6894(上限 22838)⇒ **既慢又没到位**;
手动 `scrollTop = scrollHeight` **立即 22838 一次到位**。
原因:紧邻的 DOM 全量变更让 smooth 动画的起点算在**旧**布局上。
重建后本就不该有动画 —— 用户要的是「立刻看到最新」。
**改法**:`msgsEl.scrollTop = msgsEl.scrollHeight`。
## 判据 chat-perf.test.mjs(10 条,真 Electron 跑)
新增 `npm run test-live`(需 Xvfb + electron,**不进 make test** ——
它要起真浏览器、30 秒启动,不适合当门禁)。
- 性能:200 条 < 100ms(**产品体感阈值**,不是 benchmark 数字)
- 次线性:单条成本不随规模上升
- 滚动:打开即在底部、300ms 后不被带偏
- **正确性:增量不丢消息**(5 种增删改路径)—— 比性能更重要
### 写判据时踩的坑(都记在文件里)
1. **第一版测出「0ms / 0 DOM 节点」** —— `buildChatLayout()` 在**无后端连接**时
走「请先添加连接」分支、聊天区压根没建 ⇒ **测不到**而非「不卡」。
2. **`ensureGui` 定义了但从未被调用** —— 重写文件时把调用丢了,
而定义还在,看起来一切正常。
3. **`detached: true` 只脱离进程组、不脱离会话** —— 脚本结尾 `process.exit()`
把刚起来的 GUI 带走(症状:`[tray] READY` 打了,判据却报「找不到页面」)。
改用 `setsid`。
4. **`JSON.parse(e.data)` 裸调** —— CDP 的 onmessage 也会收到非 JSON 帧,
抛在回调里既冒泡不到 await 也等不到 resolve ⇒ 整个判据挂死。
5. 我自己的滚动探针 `el.innerHTML=''` 让 `scrollHeight` 变 0,
「到位」判定是假象。改用「保留内容、只改滚动方式」重测。
## 变异测试
把 `applyIncrementalChatRender(msgsEl, html)` 改回 `msgsEl.innerHTML = html`
⇒ 判据立刻红(实测 268ms + 严格线性)。
## 门禁
- `npm run test-live`:10/10 通过
- `npm test`、`make test-gui`:全通过
- `go test ./...`:43 包 ok、0 FAIL