性能报告
3D 留着,代价挪出首屏
首页有一段实时 3D 滚动叙事。以前在中端手机上它会把主线程占满好几秒,PageSpeed Insights 甚至测不完。下面是改了什么、数据怎样,以及每一项改动付出的代价。
PageSpeed Insights 移动端
浏览器要做的工作(本地构建)
首页
- JavaScript
- 557 → 295 KB-47%
- 总传输量
- 1,018 → 438 KB-57%
- 图片
- 23 → 23 KB
- 主线程工作
- 4,435 → 1,596 ms-64%
- TBT
- 522 → 55 ms-89%
商品页
- JavaScript
- 297 → 298 KB
- 总传输量
- 721 → 560 KB-22%
- 图片
- 297 → 133 KB-55%
- 主线程工作
- 1,207 → 1,128 ms-7%
- TBT
- 45 → 48 ms+7%
商品列表
- JavaScript
- 295 → 295 KB
- 总传输量
- 515 → 515 KB
- 图片
- 102 → 102 KB
- 主线程工作
- 740 → 824 ms+11%
- TBT
- 55 → 52 ms-5%
改了什么,代价是什么
- 01
three.js 等到第一次交互才加载
首页头图先显示商品渲染图。three.js、React Three Fiber 和模型在访客第一次滚动、触摸或移动鼠标时才加载,这也正是滚动叙事开始动起来的时候。
代价:手机上第一次触摸后大约一秒,实时模型才替换掉静态图。两者是同一张渲染,所以是淡入切换,不会跳动。
- 02
画布只在滚动时渲染
以前 WebGL 画布在页面静止时也每秒画 60 帧,现在只有滚动时间线改变模型姿态时才画一帧。在没有 GPU 的机器上(比如 PageSpeed 的测试机),旧的循环让页面永远闲不下来。
代价:以后如果要加待机动画,得自己申请渲染帧。
- 03
购买区进入预渲染 HTML
商品视图自己读取 ?variant=,导致图片、标题、价格整块变成动态内容:LCP 图片要等这部分流式返回后才被发现。现在静态外壳先渲染默认款式,图片预加载放在 <head> 里,请求的款式在同一个响应里替换上去。
代价:直接打开其他颜色的链接时,网速慢的情况下可能会先闪一下默认颜色。
- 04
图片分批加载
只有进入页面时看到的那张图随页面加载。图库里的其他图片和颜色切换图等页面空闲后再挂载,之后一直保留,切换仍然是即时的。缩略图以低优先级加载。
代价:页面刚打开一秒内就悬停颜色点,可能先看到默认颜色,等图片到了才切换。
- 05
场景图预先生成多种尺寸
场景渲染图以前不管什么尺寸都给 2560 px 的 JPEG(约 180 KB)。现在用脚本生成 640/1080/1920 px 的 WebP(6–27 KB),图片 loader 选最接近的尺寸。
代价:出新渲染图时要重新跑一次脚本(scripts/scene-variants.mjs)。
怎么测的
- PageSpeed Insights 移动端:Lighthouse 13.6,模拟 Moto G Power、模拟慢速 4G,在 Google 的服务器上跑(测试地点:亚洲)。下面每个结果都有链接,谁都可以重测。
- “优化前”是已上线几个小时的版本测一次。“优化后”是部署后先跑一次预热 Vercel 边缘缓存,再跑三次,按 LCP 取中位数那次。
- 字节数和主线程耗时来自本地生产构建上的 Lighthouse 12.8(3 次取中位数),这两项几乎不受网络影响。
这些数字说明不了什么
- 商品页和列表页的 PageSpeed 差异在测试波动范围内:同一份代码不同次测试的 LCP 在 2.0 到 3.7 秒之间,而服务器响应都在 10 毫秒以内。这两页的收益在于浏览器少下载了多少(商品页图片 −55%),不能说 LCP 有可证明的变化。
- 这些是实验室数据。商店没有真实流量,所以没有真实用户数据(CrUX)来印证。
- PageSpeed 单次测试会有几分的波动,小差异当噪声看。
- Liquid 主题那一侧在开发店密码后面,PageSpeed 进不去,所以没有列在表里。
- 图片来自 Shopify 的 CDN;真实手机上的 LCP 主要取决于访客到 CDN 之间的网络。
代码:优化前 993ba71,优化后 97e7b63。