Skip to content

性能优化概述与指标 ​

优化的第一步不是「上手段」,而是知道现在几分、哪个指标差、差在哪个元素上。 这篇讲三件事:指标是什么、怎么采到真实数据、指标差了去哪里查。

一、指标是「用户体验」的代理 ​

用户只关心三件事:能不能快点看到内容、点了有没有反应、页面会不会乱跳。对应的就是三个 Core Web Vitals。

二、Core Web Vitals(现行版本) ​

指标全称衡量什么良好需改进差
LCPLargest Contentful Paint最大内容块绘制完成的时间(「看到主要内容」)≤ 2.5s2.5–4s> 4s
INPInteraction to Next Paint交互到下一次绘制完成(「点了多久有反应」)≤ 200ms200–500ms> 500ms
CLSCumulative Layout Shift累积布局偏移(「页面乱跳」)≤ 0.10.1–0.25> 0.25

⚠️ FID 已被 INP 取代 ​

FID(旧)INP(新)
测什么首次输入的延迟(只到开始处理为止)所有交互从输入到绘制完成的耗时
什么时候用2024 年 3 月前现在(已进入 CWV)
为什么淘汰只测第一次、且不含处理与渲染时间,容易「看起来很好」覆盖完整链路,更贴近真实体感

面试时如果还说 FID 是核心指标,会显得没跟上;正确说法是「FID 已被 INP 取代」。

辅助/诊断指标 ​

指标说明用途
TTFB首字节时间(网络 + 服务端)定位是不是后端/网络慢
FCP首次内容绘制白屏结束得早不早
TBT总阻塞时间(主线程被长任务占用的总和)实验室里预测 INP
SISpeed Index,视觉完成速度整体观感
TTI可交互时间老指标,仍可用于实验室分析

三、两种数据:实验室 vs 真实用户 ​

实验室(Lab)真实用户(RUM)
来源Lighthouse / DevTools / WebPageTest用户浏览器上报
优点可复现、能定位到具体代码反映真实设备、网络、地域
缺点环境单一(常在快网络+好机器上跑)有采样与上报误差
用途排查和验证优化衡量优化是否真的生效

结论:用 Lab 定位、用 RUM 验收。只看 Lighthouse 分数优化,容易优化到「跑分」而不是「体验」。

四、怎么采集(可直接用) ​

js
// vitals.js —— 用 web-vitals v4(onFID 已移除)
import { onLCP, onINP, onCLS, onTTFB, onFCP } from 'web-vitals'

function report(metric) {
  const body = JSON.stringify({
    name: metric.name,
    value: metric.value,
    rating: metric.rating,   // 'good' | 'needs-improvement' | 'poor'
    id: metric.id,
    url: location.pathname,
    // 建议再带上:设备类型、网络类型、版本号
  })
  // sendBeacon:页面关闭/跳转时也能发出,不阻塞卸载
  navigator.sendBeacon('/api/vitals', body)
}

onLCP(report)
onINP(report)
onCLS(report)
onTTFB(report)
onFCP(report)
// vitals.js —— 用 web-vitals v4(onFID 已移除)
import { onLCP, onINP, onCLS, onTTFB, onFCP } from 'web-vitals'

function report(metric) {
  const body = JSON.stringify({
    name: metric.name,
    value: metric.value,
    rating: metric.rating,   // 'good' | 'needs-improvement' | 'poor'
    id: metric.id,
    url: location.pathname,
    // 建议再带上:设备类型、网络类型、版本号
  })
  // sendBeacon:页面关闭/跳转时也能发出,不阻塞卸载
  navigator.sendBeacon('/api/vitals', body)
}

onLCP(report)
onINP(report)
onCLS(report)
onTTFB(report)
onFCP(report)

手写 PerformanceObserver(不引库) ​

js
new PerformanceObserver((list) => {
  const entries = list.getEntries()
  const last = entries[entries.length - 1]          // LCP 会持续更新,取最后一条
  console.log('LCP', last.startTime, last.element)  // element 就是罪魁祸首
}).observe({ type: 'largest-contentful-paint', buffered: true })

new PerformanceObserver((list) => {
  for (const e of list.getEntries()) {
    if (!e.hadRecentInput) console.log('CLS 贡献', e.value, e.sources)
  }
}).observe({ type: 'layout-shift', buffered: true })
new PerformanceObserver((list) => {
  const entries = list.getEntries()
  const last = entries[entries.length - 1]          // LCP 会持续更新,取最后一条
  console.log('LCP', last.startTime, last.element)  // element 就是罪魁祸首
}).observe({ type: 'largest-contentful-paint', buffered: true })

new PerformanceObserver((list) => {
  for (const e of list.getEntries()) {
    if (!e.hadRecentInput) console.log('CLS 贡献', e.value, e.sources)
  }
}).observe({ type: 'layout-shift', buffered: true })

上报设计要点:

  1. 用 sendBeacon(普通 fetch 在页面卸载时会被取消);
  2. 采样(如 10% 用户),否则数据量爆炸;
  3. 带上版本号与页面路径,否则优化前后无法对比;
  4. 取 P75(第 75 百分位)作为结论,别看平均值。

五、指标差了,去哪查 ​

LCP 慢 ​

LCP 元素通常是:大图、大段文字块、视频封面。

排查步骤怎么做
1. 找出是哪个元素DevTools Performance 面板勾选 Web Vitals,时间轴上会标 LCP;或看 largest-contentful-paint entry 的 element
2. 拆分耗时看 LCP 元素请求的四个阶段:TTFB → 资源加载 → 资源加载完到渲染 → 渲染延迟
3. 对症TTFB 慢 → 后端/CDN;资源大 → 压缩/WebP/预加载;渲染被阻塞 → 减少同步 JS、font-display: swap

INP 差(交互卡) ​

排查步骤怎么做
1. 找长任务Performance 面板看主线程的红色长任务(>50ms)
2. 看是谁占的展开长任务的调用栈,找业务函数 / 第三方脚本
3. 常见原因大列表渲染、同步 localStorage、复杂计算在主线程、第三方 SDK

手段:拆分长任务(setTimeout/scheduler.yield)、虚拟列表、计算移到 Web Worker、startTransition 降优先级。

CLS 差(布局跳动) ​

原因修法
图片/视频没写尺寸显式 width/height 或 aspect-ratio
广告/嵌入内容后插入预留固定容器高度
字体切换导致重排font-display: swap + 预加载字体,或 size-adjust
动态插入内容把下面顶走用 transform 动画代替改尺寸,或用占位骨架

排查技巧:Performance 面板里 Layout Shift 事件能看到具体移了哪些元素。

六、指标 → 手段对照表 ​

想改善优先做什么
LCP图片压缩/WebP、fetchpriority="high"、CDN、减少渲染阻塞资源、SSR/SSG
INP拆长任务、虚拟列表、Web Worker、减少第三方脚本
CLS给媒体写尺寸、字体优化、预留广告位
TTFB服务端缓存、CDN、减少重定向、数据库慢查询

七、RAIL 模型(交互设计的时间预算) ​

阶段目标用户感知
Response响应 < 100ms点击即时反馈
Animation每帧 < 16ms(60fps)动画流畅
Idle利用空闲时间 < 50ms 分片不阻塞主线程
Load首屏 < 1s(理想)快速呈现

八、速查 ​

问题答案
三个核心指标LCP、INP、CLS
FID 还能用吗不能,已被 INP 取代(2024)
INP 良好阈值≤ 200ms
CLS 良好阈值≤ 0.1
Lab 和 RUM 怎么配合Lab 定位、RUM 验收
上报用什么发navigator.sendBeacon
看什么分位P75,不是平均值
LCP 慢先看什么LCP 元素是哪张图/哪块文字,再拆四段耗时