Skip to content

25 · 前端性能度量与诊断 ​

本篇解决什么问题: "感觉有点卡"不能作为优化的依据。本篇讲清 Core Web Vitals 怎么读、用什么工具测、 怎么用火焰图定位、怎么设性能预算防劣化——先把瓶颈量化,再动刀。

📌 与 A10 的分工:本篇讲度量与诊断(找到瓶颈),A10 讲治理手段(怎么治)。


开篇速览 ​

指标衡量什么良好阈值
LCP最大内容绘制(首屏主要内容出现)≤ 2.5s
INP交互到下一次绘制(响应性)≤ 200ms
CLS累积布局偏移(视觉稳定性)≤ 0.1
FCP首次内容绘制(有东西出现)≤ 1.8s
TTFB首字节时间(服务端/网络)≤ 800ms
TBT总阻塞时间(主线程被长任务占用)≤ 200ms

一、Core Web Vitals 三兄弟 ​

1.1 LCP(Largest Contentful Paint) ​

是什么:视口内最大的文字块或图片渲染完成的时间。

常见 LCP 元素:大标题、首屏大图、视频封面。

差的典型原因:

原因表现
首屏大图太大/没优化LCP 元素就是那张图
阻塞渲染的 JS/CSS资源下载排队
服务端慢TTFB 就高
客户端渲染(CSR)JS 没执行完就没有内容(见 A13)
首屏图用了 loading="lazy"❌ 会推迟 LCP(见 A10)

1.2 INP(Interaction to Next Paint) ​

⚠️ INP 已于 2024 年取代 FID 成为核心指标。

是什么:用户交互(点击/输入/键盘)到下一帧绘制的时间,取最差的那一次(或接近最差)。

差的典型原因:

原因说明
长任务(>50ms)主线程被占,事件排队等(见 A09)
事件回调里做重活点击后同步处理大量数据
渲染阻塞交互触发了大范围重渲染
未防抖的输入每次输入都跑重逻辑

INP 分级:≤200ms 好 · 200~500ms 需改进 · >500ms 差。

1.3 CLS(Cumulative Layout Shift) ​

是什么:页面生命周期内意外布局偏移的累计分数。

典型元凶:

元凶解法
图片/视频没设尺寸必须写 width/height 或 aspect-ratio
广告/嵌入内容动态插入预留固定容器
字体加载导致文字跳动(FOUT)font-display: swap + size-adjust
在已有内容上方插入内容改为从下方插入,或用骨架屏占位

💡 CLS 只统计非用户交互引发的偏移(点击后 500ms 内的偏移不算)。


二、怎么测:实验室数据 vs 现场数据 ​

实验室(Lab)现场(Field / RUM)
工具Lighthouse、DevTools Performanceweb-vitals + 上报
环境模拟(固定网络/CPU)真实用户环境
用途调试、复现、对比优化前后反映真实体验、发现长尾问题
局限不代表真实用户难定位具体代码

🎯 正确做法:两者都要。用现场数据发现问题,用实验室数据定位与验证。

2.1 实验室:Lighthouse ​

bash
npx lighthouse https://example.com --view        # 命令行
# 或 Chrome DevTools → Lighthouse 面板

注意:

  • 结果受机器性能、扩展、缓存影响 → 用无痕模式 + 多跑几次取中位数
  • 用 CPU/网络限速模拟中低端设备(默认移动配置就是限速的)

2.2 现场:web-vitals 上报 ​

bash
npm i web-vitals
ts
import { onLCP, onINP, onCLS } from 'web-vitals'

function send(metric) {
  // 用 navigator.sendBeacon 保证页面卸载时也能发出
  navigator.sendBeacon('/api/vitals', JSON.stringify({
    name: metric.name,        // 'LCP' | 'INP' | 'CLS'
    value: metric.value,
    rating: metric.rating,    // 'good' | 'needs-improvement' | 'poor'
    id: metric.id,
  }))
}
onLCP(send); onINP(send); onCLS(send)

📌 看 P75(第 75 百分位)而不是平均值——平均值会被少数极快用户拉高,掩盖长尾问题。


三、诊断:用 Performance 面板定位 ​

3.1 录制与读图 ​

DevTools → Performance → 录制(建议勾 Screenshots + Web Vitals)
→ 操作页面 → 停止 → 看这几条泳道
泳道看什么
Main主线程任务,超过 50ms 的叫长任务(右上角标红)
Network资源下载顺序与瀑布
Frames掉帧(红色块)
TimingsFCP / LCP / DOMContentLoaded 标记
InteractionsINP 相关:哪个交互慢

3.2 定位长任务(INP 的关键) ​

  1. 在 Main 泳道找到红色角标的长任务
  2. 点开看 Bottom-Up / Call Tree,找到耗时最长的函数
  3. 常见结果:
    • Layout / Recalculate Style 很大 → 布局抖动或样式规则过多(见 A10)
    • 某个业务函数很长 → 拆分或挪到 Worker
    • XHR 后紧跟大段 JS → 数据处理太重

3.3 定位 LCP ​

  1. 在 Timings 里找到 LCP 标记
  2. 看它之前阻塞了什么:是哪个资源(大图/字体/JS)在拖
  3. 若是图片 → 检查格式、尺寸、preload、fetchpriority

3.4 定位 CLS ​

  • DevTools → Performance → 勾选 Experience → 会标出 Layout Shift 记录
  • 或 Rendering 面板 → 勾 Layout Shift Regions,页面里直接高亮抖动区域

四、性能预算:防止劣化 ​

问题:优化完过几个月又变慢了——因为没人守着。

解法:把性能变成 CI 的一道工序。

js
// lighthouse-budgets.json(示例)
{
  "resourceSizes": [
    { "resourceType": "script",     "budget": 300 },   // KB
    { "resourceType": "image",      "budget": 400 },
    { "resourceType": "total",      "budget": 1000 }
  ],
  "timings": [
    { "metric": "largest-contentful-paint", "budget": 2500 },   // ms
    { "metric": "cumulative-layout-shift",  "budget": 0.1 }
  ]
}
bash
npx lighthouse https://example.com --budget-path=./budgets.json --view
# 超预算 → 退出码非 0 → CI 失败

体积预算更简单的方式(Vite):

js
build: {
  rollupOptions: { output: { manualChunks: {...} } },
  chunkSizeWarningLimit: 500,   // 单 chunk 超过 500KB 就告警
}

🎯 预算要写进 CI,否则等于没有。团队约定"超预算不许合入"。


五、诊断方法论(排查流程) ​

① 现象:用户说"卡" → 先问清**什么操作、什么设备、什么网络**
② 量化:现场数据(web-vitals P75)确认是真的差,还是个别 case
③ 复现:DevTools 限速(CPU 4× / 4G)下复现
④ 录制:Performance 面板录制,按 LCP / INP / CLS 分类定位
⑤ 归因:
   - TTFB 高        → 服务端/CDN 问题(不是前端)
   - LCP 高         → 资源体积/阻塞/渲染模式
   - INP 高         → 长任务(主线程被占)
   - CLS 高         → 尺寸未预留/动态插入
⑥ 治理:按 [A10](./10-Web性能优化.md) 选手段
⑦ 验证:复测 + 加预算防劣化

自检清单 ​

  • [ ] 能说出 LCP / INP / CLS 各自衡量什么、良好阈值是多少
  • [ ] 知道 INP 已取代 FID
  • [ ] 能区分实验室数据与现场数据的用途,并知道看 P75
  • [ ] 能用 Performance 面板定位长任务、LCP 元素、布局偏移
  • [ ] 能把性能预算写进 CI 防止劣化

常见误区 ​

❌ 误区✅ 事实
"Lighthouse 跑 90 分就没问题"那是模拟环境;要看真实用户 P75
"看平均加载时间"平均值掩盖长尾 → 看 P75/P95
"优化完就结束了"不加预算会慢慢劣化回去了
"CLS 高是小事"直接影响体验与 SEO,而且通常很好修(加尺寸)
"INP 高就是机器慢"多数是自己的长任务占着主线程

动手练习 ​

  1. 对本站点跑 Lighthouse,记录 LCP/INP/CLS,找出 CLS 的具体元素并修复(多半是图片没尺寸)。
  2. 在 CPU 4× 降速下录一段交互,找出最长的那个任务,说出它属于哪一类(布局/脚本/样式)并给出治理手段。
  3. 接入 web-vitals 并上报到你们的监控,观察真实用户的 P75 与本地 Lighthouse 分数差多少。

相关文档 ​