-
Notifications
You must be signed in to change notification settings - Fork 0
Performance
本站做过一轮系统的前端性能专项(分支 optimize-blog-performance,28+ 个 perf commit)。
本页记录:目标、方法、实测数字、踩过的坑、复测工具。改性能前先读,别凭直觉下手。
核心结论:瓶颈往往不在「下载慢」,而在「画不出来」。先上探针拿证据,再改代码。
用户最痛的是「点击跳转要极快」(Swup SPA 跳转 / LCP / TTI)。
铁律(不可违背):界面样式是站主精心美化的,绝不阉割/删除美化组件换性能。
只优化「加载策略 + 生命周期」——防重复注册、按需加载、离页销毁。任何触碰视觉/交互的改动必须先经站主同意。
| 指标 | 优化前 | 优化后 | 变化 |
|---|---|---|---|
| 移动 4G LCP | 27236 ms | 8332 ms | -69% |
| 桌面 LCP | 960 ms | 712 ms | -26% |
| 堆净增(20 跳) | 18.9 MB | 9.7 MB | -48% |
| 监听器净增(20 跳) | 14096 | 9989 | -29% |
| 节点净增(20 跳) | 123642 | 97240 | -21% |
| 首屏字体请求 | 50 | 6 | 收敛 |
| 功能回归 | — | 8 页全 200、0 报错 | 通过 |
这是本轮最典型的「假根因 → 真凶」教学案例。
现象:移动 4G 下首屏横幅图(m1)LCP 高达 21s+。
证据链(CDP 探针实测):
m1 网络下载完成: 4.6 s
m1 解码完成(img.complete): 8.6 s
#wallpaper-wrapper 显示: 23.6 s ← 卡在这
LCP 实际绘制: ~21 s
真凶:#wallpaper-wrapper 在 SSR 时带了 display: none,要等 initWallpaperMode() 来 display: block。而这个函数挂在 DOMContentLoaded 上——移动弱网 + 4x CPU 下 DCL 要 23s 才触发。图早就下好了,就是一直被藏着。
修复(只动加载策略,不碰视觉):
- banner 模式首页 SSR 不再写
display: none(移动端非首页仍由mobile-hide-bannerclass 隐藏),首帧fadeIn={false}去掉opacity-0的 JS reveal 依赖。 - 侧栏「园径便签」背景视频(518KB
.mp4)改data-src惰性注入,PerformanceObserver监听 LCP 落定后再播放,移出 LCP 窗口。
教训:排查时先后被三个假根因带偏——氛围层 11 张图(实为旧部署缓存数据)、opacity-0 reveal(改完只动一点)、CPU 节流(1x CPU 仍 18s)。是探针的逐帧时间线戳破了直觉。
| 类别 | 手段 | 代表 commit |
|---|---|---|
| 泄漏治理 | page-lifecycle runtime(Swup 换页统一 abort/cleanup)、WalineController 离页销毁、TagCloud keep:false、14+ 组件幂等 guard |
44d6a536 a95206bc 46f9dd3b
|
| 图片加载 | 桌宠只预取当前皮、氛围层只物化当前帧、idle 预取改 hover、日历 IntersectionObserver 按需、轮播物化上限 + m1 eager/avif |
4f3d733d 63babfe6 a1e86474
|
| 覆盖率 | 三岛 client:idle、Pagefind 懒加载、内联脚本外置(assetsInlineLimit: 0) |
5ec897ec 7ded7f5f
|
| 渲染门控 | banner 首帧去 display:none / fadeIn、装饰视频 LCP 后加载 |
e60a9606 9d9b149c
|
Swup 多跳会累积匿名监听器 / observer / timer。src/lib/page-lifecycle.ts 提供 per-page epoch,换页时统一清理:
| API | 用途 |
|---|---|
bootPageLifecycle() |
幂等启动(Symbol.for 全局 guard),挂 astro:before-swap / swup:page:view
|
onPage(target, type, handler) |
带 AbortSignal 的监听,换页自动移除 |
observePage(observer) |
Mutation/ResizeObserver 换页自动 disconnect()
|
setPageTimeout / requestPageFrame
|
换页自动 clearTimeout / cancelAnimationFrame |
registerPageCleanup(fn) |
注册任意清理函数 |
用法:组件里 bootPageLifecycle() 后用上述 API 替代裸 addEventListener / setInterval。参考 src/components/comment/agent-avatar.ts。
完整探针脚本在 Codex 会话证据目录(见主仓 handoff 文档索引)。核心方法:
-
本地验证:
rm -rf dist .astro+pnpm build+python -m http.server <port>servedist/client,探针FIREFLY_PERF_BASE=http://127.0.0.1:<port>。 -
LCP 诊断:CDP
Network.requestWillBeSent抓请求发起方 +PerformanceObserver('largest-contentful-paint')拿loadTime/renderTime+ longtask/Profiler 定位主线程瓶颈。 -
泄漏:20 跳 Swup 导航后对比
JSEventListeners/Nodes/JSHeapUsedSize净增。
| 坑 | 现象 | 正解 |
|---|---|---|
本地 server 锁 dist
|
改了代码复测全假象 | 验证前 rm -rf dist .astro + 停掉 serve dist/client 的进程 |
.astro 缓存被清 |
dev server 文章列表空 / 文章页 404 | 重启 dev server(内容集合缓存重建) |
| 旧部署探针数据 | 拿着过期数据当现状 | 先确认探针打的是不是最新部署/最新 build |
| 只信「报告说快」 | 优化无数字背书 | 每项改动必须探针前后对比 |
- 主仓 handoff:
docs/outputs/handoff/perf-optimization-2026-08-09.md - Architecture · Troubleshooting · Deploy-Vercel