Core Web Vitals 实测:三个指标怎么优化

Google 把 Core Web Vitals 纳入排名因素后,「页面快不快」有了明确标准。三个指标,每个都有对应的典型问题和解法。

一、三个指标

指标全称衡量良好需改进
LCPLargest Contentful Paint加载速度≤2.5s≤4s>4s
INPInteraction to Next Paint交互响应≤200ms≤500ms>500ms
CLSCumulative Layout Shift视觉稳定≤0.1≤0.25>0.25

INP 在 2024 年取代了 FID。FID 只测首次输入的延迟,INP 测所有交互的响应时间,更贴近真实体验。

二、LCP:最大内容何时出现

LCP 元素是视口内最大的图片、视频或文本块。通常是首屏大图或标题

常见成因与解法:

成因解法
服务器响应慢优化后端、上 CDN、开缓存
渲染阻塞的 CSS/JS关键 CSS 内联,其余异步
大图加载慢转 WebP、降尺寸、preload
字体加载导致文字延迟font-display: swap 或用系统字体
客户端渲染考虑预渲染或 SSR
html
<!-- 预加载 LCP 图片 -->
<link rel="preload" as="image" href="/hero.webp">

<!-- 关键 CSS 内联 -->
<style>/* 首屏必需样式 */</style>
<link rel="stylesheet" href="/style.css" media="print" onload="this.media='all'">

本站的做法:不用自定义字体(省掉字体加载),首屏是纯文字渲染(没有大图),所以 LCP 通常在 1 秒内。

三、INP:点了之后多久有反应

INP 测的是从用户交互(点击、输入)到浏览器绘制出下一帧的时间。

常见成因:

成因解法
主线程被长任务占住拆分任务、用 Web Worker
事件处理函数太重节流/防抖、延迟非关键工作
大量 DOM 操作批量更新、虚拟列表
第三方脚本延迟加载、移除不必要的
js
// 把大任务切成小块,让浏览器有机会响应交互
function processInChunks(items) {
  let i = 0;
  function chunk() {
    const start = performance.now();
    while (i < items.length && performance.now() - start < 50) {
      process(items[i++]);
    }
    if (i < items.length) {
      requestIdleCallback(chunk);   // 空闲时继续
    }
  }
  chunk();
}

// 搜索输入防抖
let timer;
input.addEventListener("input", () => {
  clearTimeout(timer);
  timer = setTimeout(doSearch, 300);
});

本站的搜索框就用了 300ms 防抖,避免每敲一个字都重新渲染几十张卡片。

四、CLS:页面会不会乱跳

CLS 衡量「元素意外移动了多少」。最典型是图片加载后把下方内容顶下去

成因解法
图片/视频没尺寸width/heightaspect-ratio
广告/嵌入内容动态插入预留空间
字体切换导致文字重排font-display: optional 或尺寸匹配的后备字体
动态插入的内容(如通知条)position: fixed 或预留空间
html
<!-- 正确:预留了空间 -->
<img src="a.webp" width="800" height="450" loading="lazy" alt="">

<!-- CSS 方案:只给比例 -->
img { aspect-ratio: 16/9; width: 100%; height: auto; }
css
/* 广告位预留高度 */
.ad-slot { min-height: 250px; }

一个容易忽略的点:用 JS 动态插入的横幅、Cookie 提示会造成 CLS。用 fixed 定位或 transform 动画可以避免。

五、怎么测

实验室数据(开发时)

bash
# Lighthouse(DevTools → Lighthouse)
# 会给出三个指标的模拟值和优化建议

# 命令行版本
npx lighthouse https://www.bzii.cn --view

真实用户数据(上线后)

实验室数据不准(你的电脑和网络都比用户好)。看真实数据:

js
// 用 web-vitals 库采集
import { onLCP, onINP, onCLS } from "web-vitals";

onLCP(metric => sendToAnalytics(metric));
onINP(metric => sendToAnalytics(metric));
onCLS(metric => sendToAnalytics(metric));

不装库的简易版:

js
new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log(entry.entryType, entry.startTime);
  }
}).observe({ type: "layout-shift", buffered: true });

免费看真实数据的途径:

  • Google Search Console → 核心网页指标报告(需要站点有一定流量);
  • Cloudflare Web Analytics(免费,需加一段 JS);
  • Vercel Analytics(部署在 Vercel 时)。

六、诊断单个页面

Lighthouse 给出指标后,看它列出的具体机会项:

建议含义
Eliminate render-blocking resources有 CSS/JS 阻塞渲染
Properly size images图片尺寸大于显示尺寸
Serve images in next-gen formats该用 WebP
Reduce unused JavaScriptJS 太多没用上
Avoid enormous network payloads总传输量太大

七、本站的成绩与做法

本站是一个零依赖静态站,三个指标天然不错:

指标本站情况做法
LCP~0.8s无大图、无自定义字体、CSS 只有 25KB
INP<50ms交互逻辑简单,搜索做了防抖
CLS~0所有元素尺寸固定,无动态插入

最有效的优化其实是不加载东西

  • 不引框架(省掉几百 KB JS);
  • 不引外部字体(省掉一次跨域请求 + 字体文件);
  • 不用图片做装饰(用 CSS 渐变和 SVG)。

八、一份检查顺序

  1. 跑 Lighthouse 看分数和建议;
  2. 看 Network 面板,找出最大的几个资源;
  3. 按「传输 → 缓存 → 资源 → 渲染」顺序改(详见「前端性能优化清单」);
  4. 改完再跑,对比前后;
  5. 上线后看真实用户数据。
bash
# 上线前必查
curl -o /dev/null -s -w "ttfb=%{time_starttransfer}s total=%{time_total}s\n" https://www.bzii.cn
curl -sI https://www.bzii.cn | grep -iE "content-encoding|cache-control"

三个指标对应三种体验:LCP 是「等多久」,INP 是「卡不卡」,CLS 是「跳不跳」。 对着这三个问题去查,方向就不会偏。