性能监控
性能监控的目标:度量 → 发现瓶颈 → 优化 → 验证。没有度量就没有优化。
一、核心性能指标
Web Vitals(Google 标准)
| 指标 | 含义 | 目标值 |
|---|---|---|
| LCP (Largest Contentful Paint) | 最大内容绘制时间 | < 2.5s |
| INP (Interaction to Next Paint) | 交互到下次绘制 | < 200ms |
| CLS (Cumulative Layout Shift) | 累计布局偏移 | < 0.1 |
| FCP (First Contentful Paint) | 首次内容绘制 | < 1.8s |
| TTFB (Time to First Byte) | 首字节时间 | < 800ms |
旧的 FID(First Input Delay)已被 INP 取代。
业务指标
- 首屏时间:DOMContentLoaded 到首屏图片/组件渲染完成
- 白屏时间:从输入 URL 到页面出现第一个字符/像素
- 可交互时间:页面能稳定响应用户操作的时刻
- API 响应耗时:服务端接口 P50 / P95 / P99
二、性能数据采集
1. PerformanceObserver(推荐)
// LCP
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
report({ type: "lcp", value: entry.startTime });
}
}).observe({ type: "largest-contentful-paint", buffered: true });
// CLS
let clsValue = 0;
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
if (!entry.hadRecentInput) clsValue += entry.value;
}
report({ type: "cls", value: clsValue });
}).observe({ type: "layout-shift", buffered: true });
// INP
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
const eventEntry = entry as PerformanceEventTiming;
const duration = eventEntry.processingEnd - eventEntry.startTime;
report({ type: "inp", value: duration, name: entry.name });
}
}).observe({ type: "event", buffered: true, durationThreshold: 16 });
// 长任务
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
report({ type: "long_task", duration: entry.duration });
}
}).observe({ type: "longtask", buffered: true });
2. Navigation Timing
const timing = performance.getEntriesByType("navigation")[0] as PerformanceNavigationTiming;
const metrics = {
// DNS 解析耗时
dns: timing.domainLookupEnd - timing.domainLookupStart,
// TCP 连接耗时
tcp: timing.connectEnd - timing.connectStart,
// 请求响应耗时
request: timing.responseEnd - timing.requestStart,
// DOM 解析耗时
domParse: timing.domInteractive - timing.responseEnd,
// 首屏可交互
fcp: timing.domContentLoadedEventEnd - timing.fetchStart,
// 完整加载
load: timing.loadEventEnd - timing.fetchStart,
};
3. 资源加载
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.initiatorType === "img") {
report({ type: "img_load", name: entry.name, duration: entry.duration });
}
}
}).observe({ type: "resource", buffered: true });
三、上报方案
1. 关键点
- 页面卸载时上报:用
navigator.sendBeacon异步发送 - 批量上报:累积 1~2 秒或 10 条一起发,减少请求数
- 采样:线上 PV 大,按 1%~10% 采样
- 关联上下文:带上路由、版本、网络类型、用户等级等
2. 示例:批量上报
class PerformanceReporter {
private queue: any[] = [];
private timer: number | null = null;
push(data: any) {
this.queue.push(data);
if (!this.timer) {
this.timer = window.setTimeout(() => this.flush(), 2000);
}
}
flush() {
if (this.queue.length === 0) return;
const data = JSON.stringify({ events: this.queue });
navigator.sendBeacon("/api/perf", data);
this.queue = [];
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
}
}
const reporter = new PerformanceReporter();
window.addEventListener("beforeunload", () => reporter.flush());
四、性能分析工具
1. Lighthouse(本地)
npx lighthouse https://example.com --view
2. Chrome DevTools
- Performance 面板:录制运行时帧率、长任务、火焰图
- Network 面板:瀑布图、慢请求
- Coverage 面板:未使用代码占比
- Memory 面板:内存泄漏、堆快照
3. WebPageTest
- 多地域、多浏览器、真实网络环境跑分
- webpagetest.org
4. 性能监控平台
- Sentry Performance:APM + Trace
- 阿里云 ARMS
- 听云 / 听云 Browser
- 自研 + ClickHouse + Grafana
五、性能优化方向速查
构建时
- 代码分割(路由懒加载、按需引入)
- Tree Shaking
- 图片压缩、WebP、AVIF
- Gzip / Brotli
- 持久化缓存
运行时
- 关键 CSS 内联
- 字体子集化 + font-display: swap
- 长列表虚拟滚动
- 防抖节流
- Web Worker 处理计算
- 预加载(preload / prefetch)
资源
- CDN 加速
- 域名分片 / HTTP/2 多路复用
- 静态资源强缓存 + 文件 hash
体验
- 骨架屏 / loading
- 路由切换动画
- 图片懒加载
- IntersectionObserver 优化滚动监听
详细优化手段可以参考 afreshjs/性能优化
六、面试高频
- Q:LCP 怎么优化? A:优化最大元素的加载(图片 lazy → eager,CDN,SSR 预渲染),降低首屏 JS 阻塞。
- Q:CLS 是什么?什么时候会变差? A:页面元素位置/大小意外变动。常见原因:图片未设宽高、动态注入内容、字体切换(FOIT/FOUT)。
- Q:sendBeacon 和 fetch 报性能数据的区别? A:sendBeacon 是 fire-and-forget,不阻塞 unload,浏览器保证发出去;fetch 在 unload 时可能被取消。