118 lines
5.2 KiB
Markdown
118 lines
5.2 KiB
Markdown
# 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 断言。
|