Skip to main content

性能监控

性能监控的目标:度量 → 发现瓶颈 → 优化 → 验证。没有度量就没有优化。

一、核心性能指标

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

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 时可能被取消。