约 8 分钟读完 · 更新于 2026-10-21 · 文中每条都能在下面的配套工具里动手验证
LCP 回答主要内容什么时候画出来,CLS 回答页面有没有乱跳,INP 回答点了之后多久有反应。三个阈值都以 75% 分位为准:LCP 2.5 秒以内算好、超过 4.0 秒算差;CLS 0.1 以内算好、超过 0.25 算差;INP 200 毫秒以内算好、超过 500 毫秒算差。INP 在 2024 年 3 月取代 FID 成为核心指标,原因是 FID 只测输入延迟,用户点完到画面更新之间那段长任务它完全看不见。75% 分位的意思是:把一天里所有访问按指标排序,第 75% 个值决定评分,最慢的 25% 不参与。三台开发机测出来的好成绩,代表不了线上的真实分布。
| 指标 | 好 | 差 | 测量 API | 最常见原因 | 一行修法 |
|---|---|---|---|---|---|
| LCP | ≤2.5s | >4.0s | largest-contentful-paint | 首屏大图没有优先级,TTFB 偏高 | 给 LCP 图加 fetchpriority="high" 并 preload |
| CLS | ≤0.1 | >0.25 | layout-shift + hadRecentInput | 图片无尺寸,广告位后插入 | 给 img 写 width/height 或 aspect-ratio |
| INP | ≤200ms | >500ms | event + durationThreshold | 长任务阻塞主线程,第三方脚本抢执行 | 拆长任务,非关键逻辑延后到空闲时段 |
| TTFB | ≤0.8s | >1.8s | navigation timing 的 responseStart | 源站慢、没有边缘缓存 | 上 CDN,缓存 HTML 与接口响应 |
new PerformanceObserver((list) => {
const e = list.getEntries().at(-1);
console.log('LCP', Math.round(e.startTime), e.element, e.url);
}).observe({ type: 'largest-contentful-paint', buffered: true });
let cls = 0;
new PerformanceObserver((list) => {
for (const e of list.getEntries()) {
if (!e.hadRecentInput) cls += e.value; // 真实 CLS 取最大会话窗口
}
}).observe({ type: 'layout-shift', buffered: true });
new PerformanceObserver((list) => {
for (const e of list.getEntries()) {
console.log('interaction', e.name, Math.round(e.duration), e.interactionId);
}
}).observe({ type: 'event', buffered: true, durationThreshold: 40 });INP 统计的是从用户按下到浏览器画出下一帧的整段时间,包含输入延迟、事件处理、以及提交后的渲染。一个 20 毫秒的 onClick 也可能拿到 300 毫秒的 INP,因为主线程正被一个长任务占着,点击只能排队等它结束。所以优化顺序是先看有没有超过 50 毫秒的长任务,再谈回调本身。一个长任务会拖慢它之后的所有交互,包括你没改过的那部分代码。连续交互密集的页面会取高分位,交互足够多时每 50 次会忽略一个离群值。可用的手段是把长任务切到 50 毫秒以内,setTimeout 分片或 scheduler.yield 都行,首屏不需要的第三方脚本改成交互之后再加载。拆开看,输入延迟来自主线程被长任务占住,处理时间来自回调自身,展示延迟来自提交之后的样式计算与布局。
Lighthouse 是单次实验室测量:固定设备、模拟 4G 降速、冷缓存、没有真实交互,所以 INP 在 Lighthouse 里根本不会出现。CrUX 是 28 天滚动的真实用户 p75,包含低端安卓机和弱网。两者差一倍很常见。判断线上好坏以 CrUX 为准,Lighthouse 只用来定位可复现的问题。两个工具的分工是:CrUX 告诉你哪一页变差了,Lighthouse 告诉你差在哪个环节。另一个差异点是 URL 级与源站级数据:CrUX 的源站级聚合会把所有页面的分布混在一起,单页优化在源站级数字上可能完全看不出来。URL 分组样本不足时,CrUX 干脆不返回这一页的数据,你手上只剩源站级聚合值。纯展示页交互太少,CrUX 里可能压根没有 INP 数据,这种情况只能用真机手动测量补上。
给所有图片加 fetchpriority 等于没加,带宽被互相摊薄,优先级只在少数资源之间才产生差异。给首屏 LCP 图加 loading 为 lazy 会把它推迟到布局完成之后才发起请求,LCP 直接变差,lazy 只该用于首屏之外。用 font-display: block 换掉 swap,是把布局跳动换成了最长三秒的空白文字,通常更亏。顺序应该是先让 LCP 图拿到高优先级和正确尺寸,再谈其它。骨架屏能降低感知等待,但不会改变 LCP 的计时,真正的内容该什么时候画还是什么时候画。
FID 只测输入延迟,INP 一直算到下一次绘制,包含处理与渲染;INP 已于 2024 年 3 月取代 FID。
2.5 秒是 75% 分位的好阈值,超过 4.0 秒算差,中间区间属于需要改进。
取整个访问里最大的一次布局偏移会话窗口,窗口最长 5 秒、间隔小于 1 秒,并排除输入后 500 毫秒内的偏移。
Lighthouse 是单次冷启动模拟,CrUX 是 28 天真实设备数据,低端机和弱网会把 p75 拉低。