TCY 文档站性能优化设计(方案 A:低风险渐进)
- 日期:2026-03-07
- 范围:
docs/javascripts/extra.js、docs/javascripts/tcy-status-monitor.js、docs/javascripts/random-sentence.js、docs/javascripts/cursor.js - 目标:在不改变页面效果、交互行为、现有功能稳定性的前提下,降低切页后性能劣化与重复网络开销。
1. 背景与问题陈述
站点已开启 navigation.instant,页面切换属于 SPA 局部更新模型。此模式下若脚本按“整页刷新心智”书写,容易出现:
- 全局监听器重复绑定,切页次数越多越卡;
- 同一数据在短时间内重复请求(状态 API、语录 JSON);
- 常驻动画循环在空闲状态持续占用主线程。
这些问题不会立刻破坏功能,但会造成“越逛越重”的体验,尤其在移动端和低功耗设备上更明显。
2. 设计目标与非目标
2.1 目标
- 保持 UI/交互视觉效果一致;
- 保持功能行为一致(音乐、主题、目录、状态卡片、一言、粒子效果);
- 降低重复事件监听与重复请求;
- 降低空闲 CPU 占用;
- 每一步可独立回滚。
2.2 非目标
- 不重构页面架构;
- 不改导航逻辑;
- 不引入新依赖;
- 不调整文案和设计语言。
3. 方案 A(低风险渐进)
A-1:页面级监听器生命周期治理(extra.js)
现状:document$.subscribe 每次切页都会调用 tcyInitPage(),而 tcyInitPage() 内部为拖拽 TOC 等能力注册了多个 document/window 监听器。元素会被删除,但监听器可能累积。
设计:
- 引入页面级 AbortController(全局仅一个);
- 每次 tcyInitPage() 开始先 abort 上一轮,再创建新 controller;
- 将本轮绑定的 document/window/fab 监听器统一挂在 signal 上,实现自动清理。
为什么低风险:
- 不改变监听函数逻辑,不改变触发条件,只改变“解绑时机”;
- 删除旧页面节点前后都安全;
- 与 instant 生命周期天然匹配。
A-2:状态 API 短 TTL 缓存(tcy-status-monitor.js)
现状:进入包含状态面板的页面时,会对 19 个节点逐一请求;短时间切换页面会重复请求。
设计:
- 增加内存缓存(挂在 window),结构:{ ts, data: { address: onlineBool } };
- TTL 默认 60 秒;
- 若缓存未过期,直接使用缓存结果;过期再请求并更新缓存。
为什么低风险: - 渲染结构不变、文案不变; - 仅减少重复请求,不改变离线/在线判定逻辑; - TTL 短,不影响状态新鲜度。
A-3:一言 JSON Promise 缓存(random-sentence.js)
现状:每次页面订阅回调都会 fetch('/assets/data/hitokoto_bundle.json')。
设计:
- 缓存“数据 Promise”到 window 全局;
- 首次发请求,后续复用同一 Promise;
- 每次仍随机抽取一句,保证体验一致。
为什么低风险: - UI 输出逻辑不变; - 错误 fallback 保留; - 只减少网络与 JSON 解析开销。
A-4:粒子动画按需帧循环(cursor.js)
现状:requestAnimationFrame 常驻循环,即使没有粒子也持续运行。
设计: - 改为“有粒子才跑”:粒子数组为空时停止 rAF; - 鼠标移动/点击产生粒子时自动唤醒; - 页面隐藏时暂停,恢复可自动继续。
为什么低风险: - 粒子生成、更新公式与样式不变; - 只是空闲时不再空转; - 视觉表现一致。
4. 实施顺序与回滚点
extra.js(监听器生命周期)tcy-status-monitor.js(TTL 缓存)random-sentence.js(Promise 缓存)cursor.js(按需 rAF)
每完成一步执行回归;若异常,回滚该文件即可,不影响其它步。
5. 验证计划(必须)
功能回归
- 切换 10+ 次页面后:
- TOC 浮窗可开关、可拖拽、点击外部可关闭;
- 音乐播放器仍可播放/拖动/换曲;
- 状态卡片仍显示在线/离线;
- 一言正常显示;
- 光标粒子视觉一致。
性能观察
- DevTools Performance/Memory:
- 多次切页后监听器数量不再线性增加;
- Network 中状态 API 与 hitokoto JSON 重复请求显著下降;
- 空闲 CPU 占用下降(粒子空闲状态)。
6. 风险与控制
- 风险:事件绑定方式调整导致个别交互失效。
- 控制:仅改绑定生命周期,不改业务处理函数;按模块回归。
- 风险:缓存导致数据短时不更新。
- 控制:TTL 60 秒,过期自动刷新。
- 风险:rAF 停启边界导致粒子“偶发不动”。
- 控制:新增粒子时显式唤醒循环,加入可见性处理。
7. 预期收益
- 切页后性能劣化显著减轻;
- 重复请求减少,网络与 API 压力下降;
- 空闲主线程占用下降;
- 在不改视觉/交互前提下提升整体流畅度与稳定性。