前端性能指标速查手册
一、一张时间线先理清所有指标的位置
markdown
用户点链接
│
▼ 用户能交互
─────────────────────────────────────────────────────┐
0 服务器返回 DOM解析完 最大内容出现 所有资源下完
│ │ │ │ │
│ ←── TTFB ──→ │ │ │ │
│ │ ←─ DCL ─→│ │ │
│ ←──── FP / FCP ────→│ │ │
│ ←──────── LCP ─────────────────→│ │
│ ←───────────────── Load ──────────────────→ │
│ ←───────────────── TTI ──────────────────→ │
▼
导航开始(performance.timeOrigin 零点)
理解所有指标的第一件事:它们都用同一把尺子量(距离导航开始多少 ms),所以能直接比较、相减。
二、核心指标,按"谁先谁后"排列
1. TTFB · Time To First Byte
"服务器返回 HTML 第一个字节要多久"
- 回答:网络 + 服务端 有没有问题
- 包括:DNS 解析、TCP 握手、TLS 协商、服务端处理、HTML 开始返回
- 看它发现什么:后端慢、CDN 配错、DNS 查询慢、HTTPS 握手慢
测量方式
js
const nav = performance.getEntriesByType('navigation')[0];
const ttfb = nav.responseStart; // 从导航开始到收到第一个字节
达标线:< 200ms 好 / 200~600ms 可接受 / > 600ms 要改
2. FP · First Paint
"浏览器第一次往屏幕刷像素"(哪怕只是背景色)
- 实用意义不大,因为画一个灰色背景也算 FP
- 通常你会忽略它,直接看 FCP
3. FCP · First Contentful Paint(关键)
"浏览器第一次画出有意义的内容"(文本、图片、SVG、canvas 等)
- 用户视角:"白屏结束了"
- 被 Google 官方定义为 Core Web Vital 的"加载"维度之一
- 注意:SPA 里 FCP 可能是 index.html 的 loading 骨架,而不是应用真正渲染
测量方式
js
new PerformanceObserver((list) => {
list.getEntries().forEach((e) => {
if (e.name === 'first-contentful-paint') {
console.log('FCP:', e.startTime);
}
});
}).observe({ type: 'paint', buffered: true });
达标线:< 1.8s 好 / 1.8~3s 可接受 / > 3s 差
4. DCL · DOMContentLoaded
"HTML 全部解析完 + 所有 defer / module script 执行完"
- 不等图片、样式、iframe
- 对 SPA 意义比传统页面小(SPA 的 DOM 大部分是 JS 构造的,不在 HTML 里)
测量方式
js
nav.domContentLoadedEventEnd;
5. LCP · Largest Contentful Paint(Core Web Vital)
"页面主要视觉区域画完的时刻"(页面里最大的那个内容块)
- 回答:用户觉得页面"差不多好了" 是什么时候
- Google 三大 Core Web Vitals 之一,直接影响 SEO
- 会随内容变大而不断更新,直到用户首次交互才定格
- 比 FCP 更贴近真实用户体感
测量方式
js
new PerformanceObserver((list) => {
const entries = list.getEntries();
const lcp = entries[entries.length - 1]; // 最后一次上报的才是最终 LCP
console.log('LCP:', lcp.startTime, 'element:', lcp.element);
}).observe({ type: 'largest-contentful-paint', buffered: true });
达标线 :< 2.5s 好 / 2.5~4s 可接受 / > 4s 差(Google 硬标准)
6. Load
"HTML 声明的所有资源都下完"(含图、CSS、子 iframe)
- 不等:动态 import、运行时 fetch、new Image
- 在 SPA 里意义很小,常早于应用真正可用
- 看它的主要用途:检查初始 bundle + 关键资源加载是否异常
js
nav.loadEventEnd;
7. TTI · Time To Interactive
"主线程连续空闲 5 秒,可稳定响应交互"
- 传统但难精确测量(需要看 long task、FCP、资源加载多重信号)
- 浏览器不直接暴露 ------ 需要算法推断
- 建议用
web-vitals库或 Lighthouse 测,不要手写
8. INP · Interaction to Next Paint(Core Web Vital,2024 替换 FID)
"用户每次交互(点击/输入)到下一次画面更新的耗时"
- 测 可交互性(FCP/LCP 测"能看",INP 测"能用")
- 贯穿整个页面生命周期,不是首屏指标
- 反映 JS 主线程是否卡
测量方式 :强烈建议用 web-vitals 库,手写很复杂
达标线:< 200ms 好 / 200~500ms 可接受 / > 500ms 差
9. CLS · Cumulative Layout Shift(Core Web Vital)
"页面上所有意外的布局偏移累积分数"
- 图片没写 width/height、字体替换、广告插入都会引发 CLS
- 不是时间指标,是一个 相对分数
- 反映视觉稳定性
达标线:< 0.1 好 / 0.1~0.25 可接受 / > 0.25 差
10. TBT · Total Blocking Time
"FCP 到 TTI 之间,主线程被长任务阻塞的总时长"
- 长任务 = 单次执行 > 50ms 的 JS 任务
- 反映加载阶段的 JS 臃肿程度
- Lighthouse 核心指标,但不是 Core Web Vital
三、如何测 · 四种通用方案
| 方案 | 适用 | 优点 | 缺点 |
|---|---|---|---|
| Chrome DevTools Performance 面板 | 一次性调试 | 零代码、直观、火焰图详细 | 手动录制,没法长期监控 |
| Lighthouse | 一次性评分 | 综合报告,含建议 | 实验室环境,不代表真实用户 |
| 自己 PerformanceObserver | 长期埋点 | 灵活、零依赖 | 要写代码,边界 case 要自己处理 |
web-vitals 库(Google 官方) |
线上监控 | 规范、跨浏览器、支持 INP/CLS | +3KB(但值) |
推荐的最小 web-vitals 接入
js
import { onCLS, onFCP, onLCP, onINP, onTTFB } from 'web-vitals';
const send = (metric) => {
// 上报到你的监控后端
navigator.sendBeacon('/api/metrics', JSON.stringify(metric));
};
onCLS(send);
onFCP(send);
onLCP(send);
onINP(send);
onTTFB(send);
三行代码覆盖所有 Core Web Vitals,Google 官方维护,永远比自写的准。
四、自定义测量:performance.mark / performance.measure
浏览器自动提供的指标只是"公共节点"。你自己关心的节点要自己打标。
js
// 标一个时间点
performance.mark('tab-switched');
// 标一段耗时
performance.mark('data-fetch-start');
await fetchData();
performance.mark('data-fetch-end');
performance.measure('data-fetch-duration', 'data-fetch-start', 'data-fetch-end');
// 读出来
const m = performance.getEntriesByName('data-fetch-duration')[0];
console.log(m.duration);
SPA 里最常见的自定义节点:
app-script-start:JS 开始执行app-mounted:框架首屏挂载完route-change-start/route-change-end:路由跳转api-call-start/api-call-end:接口调用
五、指标挑选原则(SPA 视角)
| 你想知道 | 看这个 |
|---|---|
| 服务端/网络慢不慢 | TTFB |
| 用户白屏多久 | FCP |
| 用户真觉得"能看了"多久 | LCP |
| 首屏真正可交互多久 | 自定义 app-mounted mark |
| 每次点击是否卡顿 | INP |
| 页面是否在跳动 | CLS |
| 路由跳转多久 | 自定义 route-change measure |
| 接口慢不慢 | Resource Timing API (getEntriesByType('resource')) |
六、一句话记住每个指标的"灵魂"
- TTFB:网络+后端的成绩单
- FCP:白屏结束的那一刻
- LCP :用户觉得"页面好了"的那一刻(Google 认可的"加载")
- DCL:HTML 骨架完成的那一刻
- Load:HTML 声明的资源全下完(SPA 里不重要)
- TTI:主线程稳定空闲(理论可交互)
- INP :每次交互的响应速度(Google 认可的"交互")
- CLS :页面跳不跳(Google 认可的"稳定")
- TBT:加载阶段 JS 卡不卡
Core Web Vitals 三件套 = LCP + INP + CLS。这三个是 Google 搜索排名会用的,也是线上监控的最小必要集。其他都是辅助。
七、一条黄金路径建议
不上线:用 Chrome DevTools Performance + Lighthouse 足够,不用写埋点。
上线后 :接 web-vitals + 自建或对接 APM(Sentry / 阿里 ARMS / 自建后端)。监控 Core Web Vitals + 若干自定义 mark 就够。
不要做的事:
- 手写 TTI / CLS / INP 计算(必错)
- 在 dev 模式看性能下结论(几乎永远比 prod 慢 2~5 倍)
- 只看平均值(看 p75 或 p95 才贴近真实用户体验)