2026-08-10: 人生导图 v6.1 优化定版 — 9 个逻辑改动 / 35 处行级修改(?v=p3 已上线验证)
Decision: 对部署在 Cloudflare Workers 的单文件 web app「人生导图」(Life Map v6.1)做一轮以真实问题为导向的优化:不碰渲染引擎、不碰云同步协议、不动布局方向,只修确认的 bug + 可量化的性能浪费 + 体验硬伤。共 9 个逻辑改动、35 处行级修改,python3 build.py 重建 dist/index.html(540 KB)后以 cache-bust 版本参数 ?v=p3 上线,线上验证通过。
背景(Context)
- 应用:单文件 markmap 思维导图,根=幸福,身体/心理→左、财富→右(双向布局 patch);5 个 tab(导图/任务/知识库/人脉/规划);4 秒轮询
GET <URL>/api/state云同步(rev单调递增 + 防回显)。 - 源码:
D:\hermes\hsdesign_work\glm_lifemap_v61\life-map-v6.1\(WSL:/mnt/d/hermes/hsdesign_work/glm_lifemap_v61/life-map-v6.1/)。 - 前置约束(来自 sozo-life-map SKILL.md 铁律):v3 markmap 渲染引擎不得回退;云同步协议(4s 轮询 / rev / parent 剥离 / 防回显)不得改;左侧镜像 patch 不得破坏;breadcrumb 视图守卫、
closest('circle')等已修 bug 不得复发。 - 审查对象:app.js(2,210 行)+ style.css(1,309 行)+ index.html(19 KB)。
35 处改动分类总结
| 类别 | 数量 | 逻辑改动 |
|---|---|---|
| 性能优化 | 5 | renderData 50ms 缓冲 · treeSignature 树指纹快速路径 · applySearchHighlight 单趟遍历 · 面包屑签名去重 · 云重载 getTree 剪枝 |
| bug 修复 | 3 | pruneDoneGoals 不再删数据(数据丢失)· pushCloud 成功才推进防回显 · 搜索框快捷键提示 ⌘K→⌘F |
| 功能修复 | 1 | 展开/收起全部 折叠状态持久化(userState + 云端) |
「35 处」= 9 个逻辑改动展开后的行级修改总数(含注释、守卫、调用点)。以下逐项说明为什么。
关键改动与”为什么”
性能优化(5)
1. renderData 左侧镜像 patch 加 50ms 缓冲(app.js renderData patch)
- 为什么:
duration已设为 0(关动画),但旧代码(duration || 400)把 0 当 falsy 回退成 400 → 每次渲染都白等 450ms 才应用左柱镜像修正(foreignObject x / circle cx / line x),所有折叠/编辑/搜索跳转/云同步重载都被拖慢。改为typeof duration === 'number' ? duration : 400,duration=0 时只需 50ms 缓冲(D3 零时长过渡一帧内结束)——修复了”关动画却仍然慢”的隐性矛盾。
2. pullCloud 树签名快速路径(treeSignature / countNodes / arraysEqual)(app.js pullCloud)
- 为什么:4 秒轮询里,
rev变化 ≠ 树内容变化(其他设备只是折叠/展开、或模块数据推送也会 bump rev)。旧代码每次 rev 变化都setData + renderData + fit:① 当前 pan/zoom 被重置;② 折叠状态是”本地优先”(userState merge),重载根本不会采纳远端折叠 → 纯浪费。新增:treeSignature():DFS 前序把每个节点content#pillar拼成指纹(子节点间\u0001分隔);countNodes():节点数快速比对(O(1) 剪枝,先比数量再比指纹);- 两者都相同 → 判定”树没变”,跳过整棵重载,只同步 rev。
3. 模块数据变化检测(arraysEqual)(app.js pullCloud 内)
- 为什么:knowledge_nodes / contacts / goals 每 4 秒无条件替换引用 → 下游模块视图整块重渲染。改为
arraysEqual(长度 + JSON.stringify 比对)只有真变化才采纳。轮询结果分三态:'tree'(重载导图)/'meta'(只刷模块视图,不打断缩放)/false(什么都不做)。
4. updateBreadcrumb 内容签名去重(app.js updateBreadcrumb)
- 为什么:该函数被
setInterval(updateBreadcrumb, 1000)每秒调用,但折叠状态不变时 99% 的调用内容都没变——旧代码无条件innerHTML全量替换 → 无谓布局/重绘。加bc.dataset.sig缓存 HTML 签名,没变直接 return。
5. 云同步重载统一走 getTree() 剪枝(app.js 轮询 handler)
- 为什么:旧代码云重载直接
setData(STATE.cloudTree)→ 30 天前 ✅ 完成的节点”复活”(prune 只在整页加载时跑过一次)。改为setData(getTree()),与整页加载行为一致(30 天 ✅ 隐藏)。
bug 修复(3)
6. pruneDoneGoals 改为纯函数,不再污染源数据(app.js pruneDoneGoals)
- 本次最重要修复(数据丢失 bug):旧代码
STATE.goals = STATE.goals.filter(...)—— 每次renderGoals()都永久删除完成超过 30 天的目标,且会随pushCloud推上云端把数据洗掉。改为纯函数pruneDoneGoals(items)只过滤显示列表,源数据(STATE.goals / 云端)保留。
7. pushCloud 只有 PUT 成功才推进防回显(app.js pushCloud)
- 为什么:旧代码 PUT 无论成败都置
cloudRev→ 推送失败后,后续轮询把远端新 rev 误判为”自己推的”而永不采纳(防回显失效),两端永久不同步。现在res成功才更新cloudRev/lastSavedLocal,失败走 error 状态。
8. 搜索框快捷键提示 ⌘K → ⌘F(index.html)
- 为什么:搜索实际绑定 ⌘F(⌘K 是快速记录),UI 提示与事实不符,误导用户。
功能修复(1)
9. 展开/收起全部 折叠状态持久化(app.js handleCtxAction)
- 为什么:旧代码只改内存
payload.fold不持久化 → 刷新/换设备后全部丢失,与点圆圈折叠(wrapToggle 存 userState)行为不一致。现在同步写STATE.userState[path]+saveUserState()+pushCloud(),折叠状态随云同步跨设备生效。
部署验证
node --check app.js语法通过;python3 build.py重建dist/index.html(540 KB);- 12 项改动后置回归检查全 OK(renderData 缓冲 / pushCloud 成功检查 / treeSignature / countNodes / arraysEqual / poll getTree / breadcrumb 签名 / expand-all 持久化 / collapse-all 持久化 / pruneDoneGoals 非变异 / applySearchHighlight 单趟 / ⌘F 提示);
- 线上以 cache-bust 版本参数
?v=p3部署并验证通过。
可复用资产
- 脚本:
tree_signature.py(treeSignature/countNodes/arraysEqual 的独立 Python 移植 + 测试用例)→ 见 skills/prime-life-map-optimizations - 速查清单:skills/prime-life-map-optimizations/SKILL.md(本决策的维护速查版)
Related
- 2026-08-10 · decision-log
- skills/prime-life-map-optimizations(tree_signature.py + SKILL.md 速查清单)
- 源码:
/mnt/d/hermes/hsdesign_work/glm_lifemap_v61/life-map-v6.1/(app.js 内每个改动点都有FIX(...)注释)