← 全部文章

Core Web Vitals 实战:把 LCP 从 4.2s 降到 1.1s 的一次完整复盘

一次真实的前端性能优化记录:从测量基线、定位瓶颈到逐项优化,覆盖图片、字体、JS 执行与布局偏移,附带可复用的性能预算清单。

摘要:性能优化最怕「凭感觉改」。本文按 LCP → INP → CLS 的优先级,记录一次真实页面的优化全过程,每个步骤都给出测量方法与可复制的代码。

第一步:建立测量基线

先别改任何代码。用三个工具拿到数据:

  • Lighthouse:实验室基准,可重复
  • Chrome DevTools Performance:定位具体瓶颈
  • Web Vitals JS 库:真实用户数据

当时的基线(移动端,4G):

LCP  4.2s   ← 最大内容绘制,主目标
INP  310ms  ← 交互延迟,中目标
CLS  0.18   ← 布局偏移,超标

第二步:定位 LCP 瓶颈

LCP 元素是首屏一张 1.2MB 的 PNG。通过 Performance 面板的水印(waterfall)发现三个问题:

  1. 图片是 2400px 宽,实际只显示 640px
  2. 字体加载阻塞渲染(display: block
  3. 首屏 JS 主线程被一个分析脚本占满

第三步:逐项优化

3.1 图片:尺寸、格式、加载时机

<!-- 优化前 -->
<img src="hero.png" alt="banner" />

<!-- 优化后:响应式 + 现代格式 + 异步解码 -->
<img
  src="hero-640.webp"
  srcset="hero-640.webp 640w, hero-1280.webp 1280w"
  sizes="(max-width: 768px) 100vw, 640px"
  alt="banner"
  fetchpriority="high"
  decoding="async"
/>

收益:图片传输从 1.2MB → 48KB,LCP 直接减半

3.2 字体:预加载关键字重

<link
  rel="preload"
  href="/fonts/inter-latin-400.woff2"
  as="font"
  type="font/woff2"
  crossorigin
/>
<link
  rel="stylesheet"
  href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"
/>

display=swap + 仅预加载实际使用的字重,避免 FOIT 和多余的字体请求。

3.3 布局偏移:为动态内容预留空间

CLS 超标通常来自「内容插入时把已有内容挤开」。修复方式是为可变内容预留最小高度:

.skeleton {
  aspect-ratio: 16 / 9;
  background: linear-gradient(90deg, #eee, #fafafa, #eee);
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
}

第四步:验证与预算

优化后重新测量:

LCP  1.1s  (↓ 74%)
INP  95ms   (↓ 69%)
CLS  0.02   (✓ 达标)

为了让成绩不回退,用 Lighthouse CI 建立性能预算:

{
  "ci": {
    "assert": {
      "assertions": {
        "largest-contentful-paint": ["warn", { "maxNumericValue": 2500 }],
        "cumulative-layout-shift": ["error", { "maxNumericValue": 0.1 }],
        "total-blocking-time": ["error", { "maxNumericValue": 200 }]
      }
    }
  }
}

可复用的性能预算清单

  • 首屏无超过 100KB 的图片(WebP/AVIF)
  • 字体用 display=swap 且仅含使用字重
  • 首屏 JS ≤ 50KB gzip,第三方脚本全部 defer
  • 所有图片与可展开内容预留尺寸(aspect-ratio / min-height
  • CI 接入 Lighthouse 断言,超预算即失败

小结

性能优化的本质是「测量 → 定位 → 优化 → 固化」。把预算写进 CI,比任何口头约定都可靠。数据的每一次下降,都来自一个具体可复现的改动——而不是一次盲目的重写。