Core Web Vitals 实测:三个指标怎么优化
Google 把 Core Web Vitals 纳入排名因素后,「页面快不快」有了明确标准。三个指标,每个都有对应的典型问题和解法。
一、三个指标
| 指标 | 全称 | 衡量 | 良好 | 需改进 | 差 |
|---|---|---|---|---|---|
| LCP | Largest Contentful Paint | 加载速度 | ≤2.5s | ≤4s | >4s |
| INP | Interaction to Next Paint | 交互响应 | ≤200ms | ≤500ms | >500ms |
| CLS | Cumulative 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 |
<!-- 预加载 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 操作 | 批量更新、虚拟列表 |
| 第三方脚本 | 延迟加载、移除不必要的 |
// 把大任务切成小块,让浏览器有机会响应交互
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/height 或 aspect-ratio |
| 广告/嵌入内容动态插入 | 预留空间 |
| 字体切换导致文字重排 | font-display: optional 或尺寸匹配的后备字体 |
| 动态插入的内容(如通知条) | 用 position: fixed 或预留空间 |
<!-- 正确:预留了空间 -->
<img src="a.webp" width="800" height="450" loading="lazy" alt="">
<!-- CSS 方案:只给比例 -->
img { aspect-ratio: 16/9; width: 100%; height: auto; }/* 广告位预留高度 */
.ad-slot { min-height: 250px; }一个容易忽略的点:用 JS 动态插入的横幅、Cookie 提示会造成 CLS。用
fixed定位或 transform 动画可以避免。
五、怎么测
实验室数据(开发时)
# Lighthouse(DevTools → Lighthouse)
# 会给出三个指标的模拟值和优化建议
# 命令行版本
npx lighthouse https://www.bzii.cn --view真实用户数据(上线后)
实验室数据不准(你的电脑和网络都比用户好)。看真实数据:
// 用 web-vitals 库采集
import { onLCP, onINP, onCLS } from "web-vitals";
onLCP(metric => sendToAnalytics(metric));
onINP(metric => sendToAnalytics(metric));
onCLS(metric => sendToAnalytics(metric));不装库的简易版:
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 JavaScript | JS 太多没用上 |
| Avoid enormous network payloads | 总传输量太大 |
七、本站的成绩与做法
本站是一个零依赖静态站,三个指标天然不错:
| 指标 | 本站情况 | 做法 |
|---|---|---|
| LCP | ~0.8s | 无大图、无自定义字体、CSS 只有 25KB |
| INP | <50ms | 交互逻辑简单,搜索做了防抖 |
| CLS | ~0 | 所有元素尺寸固定,无动态插入 |
最有效的优化其实是不加载东西:
- 不引框架(省掉几百 KB JS);
- 不引外部字体(省掉一次跨域请求 + 字体文件);
- 不用图片做装饰(用 CSS 渐变和 SVG)。
八、一份检查顺序
- 跑 Lighthouse 看分数和建议;
- 看 Network 面板,找出最大的几个资源;
- 按「传输 → 缓存 → 资源 → 渲染」顺序改(详见「前端性能优化清单」);
- 改完再跑,对比前后;
- 上线后看真实用户数据。
# 上线前必查
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 是「跳不跳」。 对着这三个问题去查,方向就不会偏。
