Web 性能指标全解析:Core Web Vitals 与真实用户监控(RUM)

系统性解读 Web 性能指标体系:Core Web Vitals(LCP、INP、CLS)的测量原理、优化阈值与实战方案;TTFB/FCP/FID/TBT 等辅助指标;真实用户监控(RUM)与实验室测试(Lighthouse)的结合;Web Vitals JS API、Chrome UX Report(CrUX)、PerformanceObserver、Resource Timing;Vue/React 性能监控 SDK 接入。

“你无法优化你不测量的东西。” Web 性能优化的第一步是建立指标体系,区分「实验室数据」与「真实用户数据」,用数据驱动决策。


一、性能指标全景图

1.1 核心指标 vs 辅助指标

性能指标体系
├── Core Web Vitals(核心)
│   ├── LCP — 最大内容绘制(加载性能)
│   ├── INP — 交互到下一次绘制(交互响应)
│   └── CLS — 累积布局偏移(视觉稳定性)
│
├── 辅助指标(诊断用)
│   ├── TTFB — 首字节时间(服务器响应)
│   ├── FCP — 首次内容绘制(首屏感知)
│   ├── TBT — 总阻塞时间(主线程繁忙度)
│   └── FID — 首次输入延迟(已废弃,被 INP 替代)
│
└── 业务指标
    ├── 跳出率
    ├── 转化率
    └── 用户留存

1.2 指标测量方式对比

方式数据采集代表环境用途
实验室测试受控环境Lighthouse、WebPageTest开发阶段诊断、CI 回归
真实用户监控(RUM)真实用户浏览器Web Vitals API、CrUX生产环境基线、趋势分析

二、Core Web Vitals 详解

2.1 LCP(Largest Contentful Paint)— 加载性能

衡量最大可见内容元素的渲染时间。

LCP 候选元素:
├── <img>(含通过 CSS background-image 加载的)
├── <image>(SVG 内)
├── <video> 的 poster 图
└── 含有文本节点的块级元素(如 <h1>, <p>, <div>)

❌ 不包含:
    - 非可见元素(opacity:0, visibility:hidden)
    - 全视口背景图(可能不视为内容)
    - CSS 渐变

阈值

评级范围含义
🟢 良好≤ 2.5s75% 的用户体验在此范围内
🟡 需改进2.5s ~ 4.0s
🔴 差> 4.0s

优化方向

<!-- 1. 图片优化:用现代格式 + 预加载关键资源 -->
<link rel="preload" as="image" href="/hero.avif" type="image/avif">
<img src="/hero.avif" width="1200" height="600" alt="Hero" fetchpriority="high">

<!-- 2. 字体优化:防止 FOIT/FOUT -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>

2.2 INP(Interaction to Next Paint)— 交互响应

衡量用户交互到下一次渲染的延迟,替代已废弃的 FID。

// INP 测量所有交互(点击、触摸、键盘),取最差的延迟
// 交互生命周期:
// 用户点击 → 事件处理(JS) → 样式计算 → 布局 → 绘制 → 合成
//                           ↑ INP 测量这一段总时间

阈值

评级范围
🟢 良好≤ 200ms
🟡 需改进200ms ~ 500ms
🔴 差> 500ms

优化方向

// ❌ 阻塞主线程的长任务
function handleClick() {
  heavyComputation(); // 500ms+,导致 INP 飙升
}

// ✅ 分解长任务(yield to main)
async function handleClick() {
  await scheduler.yield(); // Chrome 96+,或 polyfill
  heavyComputationPart1();
  await scheduler.yield();
  heavyComputationPart2();
}

// ✅ 使用 Web Worker
const worker = new Worker('/worker.js');
button.addEventListener('click', () => {
  worker.postMessage(data); // 耗时操作移出主线程
});

2.3 CLS(Cumulative Layout Shift)— 视觉稳定性

衡量页面生命周期中累积的布局偏移分数

CLS = Σ (位移距离 / 视口高度) × (受影响元素面积 / 视口面积)

示例:
  - 图片未设尺寸,加载后推挤下方内容 → CLS > 0
  - 广告位异步插入,导致内容跳动 → CLS > 0
  - 字体加载导致文本重排 → CLS > 0

阈值

评级范围
🟢 良好≤ 0.1
🟡 需改进0.1 ~ 0.25
🔴 差> 0.25

优化方向

<!-- 1. 为图片/视频/iframe 预留空间 -->
<img src="photo.jpg" width="800" height="600" style="aspect-ratio: 4/3;">

<!-- 2. 为广告位预留容器 -->
<div class="ad-slot" style="min-height: 250px;">
  <!-- 广告异步加载,但容器高度已定 -->
</div>

<!-- 3. 避免在现有内容上方插入内容 -->
<!-- 错:在列表顶部插入新项目,推挤全部已有内容 -->
<!-- 对:在列表底部追加,或用动画平滑过渡 -->

三、辅助指标详解

3.1 TTFB(Time to First Byte)

从请求发出到收到第一个字节的时间。主要反映服务器端 + 网络传输性能。

评级范围优化手段
🟢 良好≤ 800msCDN、Edge SSR、缓存
🟡 需改进800ms ~ 1.8s
🔴 差> 1.8s

3.2 FCP(First Contentful Paint)

第一个文本或图片内容渲染到屏幕的时间。反映用户感知到的「页面开始加载」

3.3 TBT(Total Blocking Time)

FCP 到 TTI(Time to Interactive)之间,主线程被阻塞超过 50ms 的任务总和。TBT 与 INP 高度相关


四、真实用户监控(RUM)

4.1 web-vitals 库接入

npm install web-vitals
// src/lib/vitals.ts
import { onLCP, onINP, onCLS, onTTFB, onFCP } from 'web-vitals';
import { sendToAnalytics } from './analytics';

export function initVitalsTracking() {
  // 所有指标都报告,带 rating(good/needs-improvement/poor)
  onLCP(sendToAnalytics, { reportAllChanges: true });
  onINP(sendToAnalytics, { reportAllChanges: true });
  onCLS(sendToAnalytics, { reportAllChanges: true });
  onTTFB(sendToAnalytics);
  onFCP(sendToAnalytics);
}

// 上报函数
function sendToAnalytics(metric: Metric) {
  const body = JSON.stringify({
    name: metric.name,           // 'LCP' | 'INP' | 'CLS' | ...
    value: metric.value,         // 测量值
    rating: metric.rating,       // 'good' | 'needs-improvement' | 'poor'
    delta: metric.delta,
    id: metric.id,
    navigationType: metric.navigationType,
    // 附加上下文
    url: location.href,
    userAgent: navigator.userAgent,
    timestamp: Date.now(),
  });

  // 使用 sendBeacon 可靠上报
  if (navigator.sendBeacon) {
    navigator.sendBeacon('/api/vitals', body);
  } else {
    fetch('/api/vitals', { body, method: 'POST', keepalive: true });
  }
}

4.2 Vue 项目集成

// src/plugins/vitals.ts
import { onLCP, onINP, onCLS } from 'web-vitals';

export default {
  install() {
    if (import.meta.env.SSR) return;

    onLCP(console.log);
    onINP(console.log);
    onCLS(console.log);
  }
};

// main.ts
import vitalsPlugin from './plugins/vitals';
app.use(vitalsPlugin);

4.3 React 项目集成

// components/WebVitals.tsx
'use client';
import { useEffect } from 'react';
import { onLCP, onINP, onCLS } from 'web-vitals';

export function WebVitals() {
  useEffect(() => {
    onLCP(console.log);
    onINP(console.log);
    onCLS(console.log);
  }, []);

  return null;
}

// layout.tsx
import { WebVitals } from './components/WebVitals';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <WebVitals />
        {children}
      </body>
    </html>
  );
}

4.4 PerformanceObserver 原生 API

// 低级 API:直接监听性能条目
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log(entry.name, entry.startTime, entry.duration);
  }
});

// 监听核心指标
observer.observe({ type: 'web-vitals' });     // LCP, FCP, CLS
observer.observe({ type: 'longtask' });        // 长任务(>50ms)
observer.observe({ type: 'resource' });        // 资源加载详情

// 获取 Navigation Timing
const navEntries = performance.getEntriesByType('navigation');
const nav = navEntries[0] as PerformanceNavigationTiming;
console.log({
  dns: nav.domainLookupEnd - nav.domainLookupStart,
  tcp: nav.connectEnd - nav.connectStart,
  ttfb: nav.responseStart - nav.requestStart,
  download: nav.responseEnd - nav.responseStart,
  domReady: nav.domContentLoadedEventEnd - nav.startTime,
  loadComplete: nav.loadEventEnd - nav.startTime,
});

五、Chrome UX Report(CrUX)

Google 提供的真实网页性能公开数据集,基于 Chrome 用户的实际体验。

# 用 CrUX API 查询网站性能
curl -s "https://chromeuxreport.googleapis.com/v1/records:queryRecord?key=$API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"origin": "https://example.com"}'

数据来源

  • PageSpeed Insights(内置 CrUX 数据)
  • Google Search Console(Core Web Vitals 报告)
  • Chrome DevTools(Performance 面板的 Metrics 侧边栏)

六、Lighthouse 诊断流程

6.1 运行与解读

# CLI 运行
npx lighthouse https://example.com --output=json --output-path=report.json

# CI 集成(性能预算)
npx lighthouse https://example.com \
  --budget-path=budget.json \
  --preset=desktop
// budget.json
[{
  "path": "/*",
  "resourceSizes": [
    { "resourceType": "script", "budget": 300 },
    { "resourceType": "stylesheet", "budget": 100 },
    { "resourceType": "image", "budget": 500 }
  ],
  "timings": [
    { "metric": "largest-contentful-paint", "budget": 2500 },
    { "metric": "interactive", "budget": 3500 }
  ]
}]

6.2 优化的优先级

Lighthouse 诊断 → 定位瓶颈 → 针对性优化

TTFB 差         → 服务器/Edge/CDN 优化
LCP 差          → 优化关键资源加载顺序、压缩图片、预加载
CLS 差          → 图片尺寸、字体加载策略、动态内容预留空间
INP/TBT 差      → 拆分长任务、减少主线程 JS、Web Worker
资源体积大      → Tree-shaking、代码分割、压缩、CDN

七、性能监控看板设计

// RUM 数据聚合示例(后端接收端)
interface VitalsReport {
  name: 'LCP' | 'INP' | 'CLS' | 'TTFB' | 'FCP';
  value: number;
  rating: 'good' | 'needs-improvement' | 'poor';
  url: string;
  deviceType: 'mobile' | 'desktop' | 'tablet';
  connection: '4g' | '3g' | 'slow-2g';
  timestamp: number;
}

// 后端聚合(按 URL + 时间窗口)
// 输出:P75 LCP = 1.8s,P95 LCP = 4.2s
// 趋势图:日/周/月的性能变化
// 告警:P75 LCP > 2.5s 连续 5 分钟触发

八、性能指标速查表

指标全称测量什么目标值工具
LCPLargest Contentful Paint最大可见内容渲染≤ 2.5sLighthouse, RUM
INPInteraction to Next Paint交互到下一次绘制≤ 200msRUM(推荐)
CLSCumulative Layout Shift累积布局偏移≤ 0.1Lighthouse, RUM
TTFBTime to First Byte首字节时间≤ 800msRUM
FCPFirst Contentful Paint首次内容绘制≤ 1.8sLighthouse, RUM
TBTTotal Blocking Time总阻塞时间≤ 200msLighthouse

参考与延伸阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「frontend」更多文章

  1. API 设计与 BFF 层:REST、GraphQL、tRPC 选型与前后端协作
  2. WebAssembly 前端工程化实践:编译链、性能对比与混合架构
  3. 现代浏览器 API 与 Web 平台能力地图