最近做了一个基于 Next.js 、React 和 PixiJS 的网页小游戏 Color Tiles。
页面看起来不复杂:一个 WebGL 棋盘、几个控制按钮,加上一些玩法介绍。但第一次跑移动端 Lighthouse 时只有四十多分。最初的报告没有保存下来,目前仓库里能复核的中间结果是 59 分:
- LCP:11.36 s
- TBT:625 ms
- 首屏传输量:2.33 MB
经过几轮优化后,生产构建的移动端 Lighthouse 达到 97 分,桌面端 100 分:
- LCP:2.29 s ,下降约 80%
- TBT:135 ms ,下降约 78%
- 首屏传输量:509 KB ,下降约 78%
- CLS:保持为 0
这次比较有效的改动主要有这些:
- 不再整包引入
pixi.js-legacy,改为从@pixi/core、@pixi/display、@pixi/sprite等模块按需引入,并直接使用 WebGL Renderer 。 - 把只能在浏览器运行的游戏模块改成动态加载,同时保留尺寸稳定的 loading 界面,避免布局跳动。
- 把教程 GIF 转成 MP4 ,并延迟到用户真正打开教程时再加载。
- 图片转为 WebP ,补全响应式
sizes,并通过 Cloudflare Image Resizing 输出适合设备尺寸的版本。 - 把图片迁移到独立的
img.colortilesgame.netCDN 子域名,静态资源缓存延长到一年,并使用immutable。同时用preconnect提前完成新域名的连接。 - 开启 Next.js 的实验性
inlineCss,把 CSS 放进 HTML ,减少首次渲染中的一次阻塞请求。 - 排行榜 API 改为接近视口或游戏结束后再请求,不再与棋盘初始化争抢首屏资源。
过程中也踩了几个坑。例如,一开始 preload 了完整背景图,以为可以改善 LCP ,结果大图反而提前占用网络。后来只 preload 4.8 KB 的占位图,效果更合理。
另一个体会是,不要只盯着 Lighthouse 总分。中间报告的 FCP 已经不到 1 秒,真正的问题是 11 秒的 LCP 、主线程阻塞和过大的资源量。把非关键工作移出首屏,比继续抠 FCP 更有效。
线上版本:
更完整的过程、代码片段、数据和取舍写在 Medium:
欢迎交流 WebGL 游戏、Next.js 或 Lighthouse 优化方面的经验。