Files
mnote/bugs/04-tree-domain/process/4-25-tree-command-create-delete-reload-latency-v1.md
T

118 lines
5.2 KiB
Markdown
Raw Normal View History

2026-05-13 22:43:16 +08:00
# 4-25 [process][bug] 树命令新建/删除后强制刷新导致卡顿 v1
> 更新时间:2026-05-13
>
> 分类归属:
> - `04-tree-domain/process`
> - 关联缺陷:`bugs/05-editor-mainline/process/5-11-mindmap-ghost-assets-and-tree-command-latency-v1.md`
>
> 用户反馈:
> - “删除页面和新建页面都很慢,不知道为什么这么卡。”
## 1. 问题定义
当前页面树/文件树的新建页面、删除页面等命令,在服务端命令成功后仍会触发整页刷新或完整树刷新。用户感知是:新建和删除并不是局部更新,而是卡一下、等整套页面壳或树壳重新加载。
这属于 `tree command -> projection update -> UI apply` 链路问题,应归到 `04-tree-domain`,而不是只作为编辑器 UI 问题处理。
## 2. 真实现象
用户反馈:
1. 新建页面慢。
2. 删除页面慢。
3. 卡顿与同轮 mindmap 文件树重复增长一起出现,怀疑树刷新链路被频繁触发。
期望结果:
1. `tree.node.create` 成功后,树 UI 局部插入新节点并进入重命名/导航状态。
2. `tree.node.archive` 成功后,树 UI 局部移除节点或移动到回收站 projection。
3. 只有 delta 无法应用、projection 缺失、SSE 断线或数据不一致时,才进入 resync/reload fallback。
4. 普通新建/删除不应默认整页 reload。
## 3. 证据
### 3.1 Rust tree shell 有硬刷新路径
`rust/crates/mnote-web/src/routes/tree.rs:2976``:2986`
- `scheduleRefresh()` 在 80ms 后执行 `window.location.reload()`
- 如果带 `renameRowId`,则通过 `window.location.assign(url.toString())` 重新加载。
调用点:
- 文件树删除:`rust/crates/mnote-web/src/routes/tree.rs:2791``:2824`
- 新建页面:`rust/crates/mnote-web/src/routes/tree.rs:4309``:4340`
- 重命名:`rust/crates/mnote-web/src/routes/tree.rs:4373``:4406`
- 移动:`rust/crates/mnote-web/src/routes/tree.rs:4448``:4502`
这条路径会让 tree shell 命令体验明显慢于纯 delta / reducer 更新。
### 3.2 主页面已有 tree live controller,但命令后仍刷新
主页面已经监听 tree live
- `rust/crates/mnote-web/src/ssr/pages/layout.rs:3881``:3917` 处理 `tree:snapshot` / `tree:delta` / `tree:resync`
- `rust/crates/mnote-web/src/ssr/pages/layout.rs:3939``:4050` 启动 `/api/tree/events` EventSource
也就是说,命令完成后理论上可以由 delta/resync 更新 projection;当前硬 reload 与 live projection 机制重复。
### 3.3 命令本身已有 delta hint
Bridge runtime 对树命令生成 stream delta hint
- `tree.node.create``rust/crates/bridge-runtime/src/lib.rs:9545``:9592`
- `tree.node.archive``rust/crates/bridge-runtime/src/lib.rs:9655``:9702`
当前前端没有把这些结果作为默认局部更新来源,而是在 tree shell 中继续 reload。
### 3.4 React Sidebar 路径也存在重刷链
子代理只读调查发现 React Sidebar 路径也存在重刷:
- 新建页面:`wolai-frontend/src/components/sidebar/sidebar.tsx:1799``:1847` 附近在 command 后 `await refreshTree()` 再跳转。
- 删除页面:`wolai-frontend/src/components/sidebar/sidebar.tsx:2148` 附近删除后等待 command、`refreshTree()`、广播文档变化/跳转。
- Next API 的 `api/tree/commands` create/delete 还会执行 workspace/default scaffold、bridge mutation、artifact 记录等多段流程。
这说明卡顿不只来自单个 reload,而是命令后刷新链路偏重。
## 4. 当前判断
当前慢的核心不是“Convex 一定慢”,而是树命令执行后缺少轻量本地 apply:
1. 命令 result / delta hint 已经具备局部更新信息。
2. UI 仍经常走 `refreshTree()``window.location.reload()` 或 full snapshot。
3. `resync_required` 类事件会触发完整 workspace snapshot,结构性变化越多,越容易造成卡顿。
## 5. 建议验证
修复前先补一条计时 smoke
1. 登录真实测试账号。
2. 新建 `TEST-TREE-LATENCY-<timestamp>` 页面。
3. 记录 `POST /api/tree/commands` 响应耗时。
4. 记录下一次 `tree:delta` / `tree:resync` 到达耗时。
5. 断言过程中是否发生 `window.location.reload()` 或顶层 navigation。
6. 删除该测试页面,重复同样计时。
7. 输出新建/删除从点击到 DOM 稳定的总耗时。
## 6. 建议修复方向
1. `tree.node.create` 成功后直接用 command result 插入本地节点,并只在后台等待 live delta 校准。
2. `tree.node.archive` 成功后直接从当前 projection 移除节点,并只在后台等待 live delta 校准。
3. `scheduleRefresh()` 改成显式 fallback,只有 reducer 无法应用时才调用。
4. React Sidebar 的 `refreshTree()` 应去重、节流,并避免与 SSE/full snapshot 同时触发。
5. smoke 增加无 reload 断言和耗时阈值。
## 7. 流转条件
当前状态:`process`
只有满足以下条件后才能移动到 `bugs/04-tree-domain/done/`
1. [ ] 新建页面普通路径不再默认整页 reload。
2. [ ] 删除页面普通路径不再默认整页 reload。
3. [ ] command result / tree delta 能局部更新页面树与文件树 projection。
4. [ ] fallback reload 只在明确异常条件下触发,并有可观测标记。
5. [ ] smoke 记录新建/删除耗时,并通过无 reload 断言。