Core Web Vitals 实战:LCP 从 4.2 秒到 1.3 秒的完整优化过程

Core Web Vitals 实战:LCP 从 4.2 秒到 1.3 秒的完整优化过程

admin
2026-11-02 / 0 评论 / 4 阅读 / 约 7 分钟 / 正在检测是否收录...

背景:为什么 LCP 成了瓶颈

去年底,我们接手了一个内容型电商项目。上线后 Google Search Console 的 Core Web Vitals 报告显示:移动端 LCP 中位数为 4.2 秒,CLS 0.21,INP 280ms。三项指标全部标红,其中 LCP 是最大短板。

Core Web Vitals 包含 LCP(最大内容绘制)、CLS(累积布局偏移)和 INP(交互到下一次绘制)。LCP 衡量加载性能,CLS 衡量视觉稳定性,INP 衡量交互响应能力。

我们的目标很明确:把 LCP 压到 2.5 秒以内,同时把 CLS 和 INP 拉回绿色区间。


诊断:用数据锁定关键渲染路径

1. 实验室与现场数据结合

先用 Lighthouse 和 WebPageTest 跑基线,同时接入 web-vitals 库收集真实用户监控(RUM)数据。

import { onLCP, onCLS, onINP } from 'web-vitals';

function sendToAnalytics(metric) {
  const body = JSON.stringify({
    name: metric.name,
    value: metric.value,
    id: metric.id,
    delta: metric.delta,
  });
  // 使用 sendBeacon 避免阻塞卸载
  navigator.sendBeacon('/analytics', body);
}

onLCP(sendToAnalytics);
onCLS(sendToAnalytics);
onINP(sendToAnalytics);

2. 定位 LCP 元素

通过 PerformanceObserver 捕获 LCP 候选元素,发现移动端 LCP 是首屏轮播图的第一张图片,桌面端则是 H1 标题。

进一步分析瀑布图,发现三个核心问题:

  • 关键渲染路径过长:CSS 阻塞渲染,首屏图片请求发起太晚(被 JS 和字体请求挤占)。
  • 图片未优化:首屏图片是 1.8MB 的 PNG,且没有设置 fetchpriority。
  • 字体加载导致 FOIT:自定义字体在 3.2 秒才可用,期间文字不可见。

优化实施:六个关键动作

1. 关键渲染路径优化

  • 内联关键 CSS:将首屏渲染所需的 CSS 提取出来,直接内联到 <head>,其余 CSS 异步加载。
  • 延迟非关键 JS:把非首屏交互的 JS 标记为 defer 或动态 import()。
  • 预连接关键域名:对图片 CDN、字体 CDN 添加 preconnect 和 dns-prefetch。
<link rel="preconnect" href="https://cdn.example.com" crossorigin>
<link rel="dns-prefetch" href="https://fonts.example.com">
<link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/hero.avif" as="image" fetchpriority="high">

2. 图片优化

  • 首屏图片改用 AVIF/WebP,配合 <picture> 回退。
  • 使用 srcset 和 sizes 提供响应式图片,移动端只加载 800px 宽版本。
  • 显式设置 width 和 height 避免 CLS。
  • 对 LCP 图片设置 fetchpriority="high",并移除 loading="lazy"。

3. 字体优化

  • 使用 font-display: swap 避免 FOIT。
  • 子集化字体,只保留拉丁字符和常用符号,体积从 180KB 降到 28KB。
  • 预加载关键字体文件。

4. JS 拆包与按需加载

  • 用 Webpack 的 splitChunks 将 vendor 和业务代码分离。
  • 路由级懒加载,首屏只加载必要模块。
  • 移除未使用的 polyfill,使用 browserslist 精准控制目标浏览器。

5. CDN 与缓存策略

  • 静态资源全部上 CDN,开启 Brotli 压缩。
  • 对带 hash 的文件设置 Cache-Control: max-age=31536000, immutable。
  • HTML 使用 Cache-Control: no-cache 配合 ETag。

6. 预连接与预加载

  • 对第三方域名(支付、统计)使用 preconnect。
  • 对首屏关键资源使用 preload,但避免过度预加载导致带宽竞争。

优化前后数据对比

指标优化前优化后变化
LCP(移动端)4.2s1.3s-69%
CLS0.210.02-90%
INP280ms90ms-68%
首屏图片体积1.8MB120KB-93%
字体加载时间3.2s0.4s-87%
JS 首包体积450KB180KB-60%
数据来自 30 天 RUM 采样,移动端 4G 网络,样本量约 12 万次页面浏览。

常见问题

LCP 优化后,为什么 INP 也提升了?

因为 JS 拆包减少了主线程阻塞,交互事件能更快被处理。INP 对长任务非常敏感,首包体积下降直接降低了长任务出现的概率。

预加载所有关键资源会不会适得其反?

会。preload 会占用带宽和优先级,如果预加载了非首屏资源,反而会推迟 LCP 图片的加载。建议只对 LCP 图片、关键字体和首屏 CSS 使用。

图片已经用了 WebP,为什么 LCP 还是慢?

检查是否设置了 fetchpriority="high",以及是否被 loading="lazy" 延迟。另外,图片 CDN 的响应时间、是否命中缓存也会影响 LCP。

CLS 优化后还会反弹吗?

会。如果后续新增了动态插入的内容(如广告、推荐位),没有预留空间,CLS 会再次升高。建议在 CI 中加入 Lighthouse 断言,防止回归。

0

评论 (0)

取消