把 GIF 换成 MP4,Lighthouse 分数从 64 提到 92 的实录
我们有一张营销落地页,首屏放着三段动画 GIF —— 一段展示产品工作的主视觉循环,加两段功能 动画。合计 11 MB。Lighthouse 给手机端打了 64 分,LCP 4.2 秒,Total Blocking Time 380 ms。把这三段 GIF 换成 MP4 —— 视觉内容相同、循环行为相同 —— 一个下午就把分数推到 了 92。本文是这次操作的全程记录。
起点
页面是一个相当标准的 Next.js 营销站。Tailwind、静态生成、部署到 Vercel。三段 GIF 都是从 After Effects 用 720p、30fps、中等优化导出的。大小:
- 主视觉动画(5 秒,720p):5.8 MB
- 功能 1(3 秒,600p):2.9 MB
- 功能 2(4 秒,600p):2.4 MB
动画总传输量:11.1 MB。在 Slow 4G(Lighthouse 移动端档位)下,差不多要 27 秒才能完整加载。 页面文本很快渲染出来,但主视觉一直停在"加载中"的占位上太久 —— 这正是 LCP 要扣分的状态。
起点的 Lighthouse 报告
| 指标 | 分数 |
|---|---|
| Performance | 64 |
| Largest Contentful Paint | 4.2 秒 |
| Total Blocking Time | 380 ms |
| Speed Index | 5.8 秒 |
| Cumulative Layout Shift | 0.04 |
转换
我们用两套设置把每段 GIF 都跑了 ffmpeg:H.264 的 MP4 用作广覆盖兜底,VP9 的 WebM 给支持 的浏览器更小的传输量。命令:
ffmpeg -i hero.gif -movflags faststart -pix_fmt yuv420p -crf 22 -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2:flags=lanczos" -an hero.mp4
ffmpeg -i hero.gif -c:v libvpx-vp9 -crf 30 -b:v 0 -deadline good -cpu-used 2 -row-mt 1 -pix_fmt yuv420p -an hero.webm
转换后的体积令人印象深刻:
| 资源 | GIF | MP4 (H.264) | WebM (VP9) |
|---|---|---|---|
| 主视觉 | 5.8 MB | 320 KB (-94%) | 240 KB (-96%) |
| 功能 1 | 2.9 MB | 180 KB (-94%) | 140 KB (-95%) |
| 功能 2 | 2.4 MB | 150 KB (-94%) | 110 KB (-95%) |
总传输量从 11.1 MB 降到 650 KB(H.264),VP9 下是 490 KB。17–22 倍的削减。
标记的改动
原标记是 <img src="hero.gif" alt="...">。替换后:
<video autoplay loop muted playsinline poster="hero-poster.jpg">
<source src="hero.webm" type="video/webm">
<source src="hero.mp4" type="video/mp4">
</video>
poster 是视频开始解码前显示的一张静帧(JPEG,约 30 KB)。这是解锁 LCP 的关键 —— 浏览器会把这张 poster 当作"有内容的绘制",于是 1 秒内就达到了。
转换后的 Lighthouse 报告
| 指标 | 前 | 后 |
|---|---|---|
| Performance | 64 | 92 |
| LCP | 4.2 秒 | 1.4 秒 |
| TBT | 380 ms | 110 ms |
| Speed Index | 5.8 秒 | 1.9 秒 |
| CLS | 0.04 | 0.02 |
LCP 降了 67%,TBT 降了 71%。TBT 改善的原因:软件 GIF 解码原本在每一帧都阻塞主线程;硬件 MP4 解码会卸载到单独的进程。
注意事项与坑
- autoplay 需要 muted。iOS 和 2018 年以后的多数浏览器都会拦下含音视频的 自动播放。autoplay 的循环视频上始终设
muted。 - iOS 需要 playsinline。否则 iOS 在轻触时会把视频拉成全屏播放器。
- H.264 要求偶数尺寸。该编码器要求宽高都被 2 整除。用
scale=trunc(iw/2)*2:trunc(ih/2)*2滤镜。 - Safari 需要 yuv420p。其他像素格式 Chrome 能解,Safari 会静默失败。
- 不要把视频用于静态装饰。如果"动画"几乎不动(缓慢的 zoom、轻微的 pan), 静态图或 CSS 动画都更便宜。
Lighthouse 之外:真实用户层面的影响
Lighthouse 的数字是合成分。真正要看的是现场数据 —— 真实网络中的真实访客。改动后两周内 我们看到:
- 线上 p75 LCP:3.8 秒 → 1.6 秒(Chrome User Experience Report)
- 跳出率:移动端 -8%(GA 数据)
- CDN 流量账单:仅营销站每月 -$340
带宽节省其实比不上之前用调色板技巧和抖动死磕 GIF 浪费掉的工程时间。一旦跨过格式边界, 那些挣扎都不重要了。
怎么在自己站点上做这件事
把分数从 64 推到 92 的五步:
- 在 DevTools 的 Network 面板里找出页面上每一个
.gif。 - 把每一个都转成 MP4 (H.264) 和 WebM (VP9)。一次性可以用我们的 GIF → MP4 转换工具,两种格式都能出,全程跑在浏览器 内。要进流水线就用上面的 ffmpeg 命令。
- 为每段视频生成一张静帧 JPEG poster。
ffmpeg -i input.gif -vf "select=eq(n\\,0)" -vframes 1 poster.jpg。 - 按上面的写法把每个
<img>换成<video>。 - 重新跑 Lighthouse。确认分数跳了,LCP 降了。
总耗时:我们维护的站点里,每页大约 20 分钟。在数千用户的每次页面访问上复利累积下来,这是 我们这一年里做过的单笔回报最高的性能修复。