跳到主要内容

性能监控与Web-Vitals

1. Core Web Vitals(Google 三件套)

指标含义良好
LCP(Largest Contentful Paint)最大内容绘制< 2.5s> 4s
INP(Interaction to Next Paint)交互响应(取代 FID)< 200ms> 500ms
CLS(Cumulative Layout Shift)累积布局偏移< 0.1> 0.25

辅助:

  • FCP(First Contentful Paint):首次内容绘制
  • TTFB(Time to First Byte):首字节
  • TTI(Time to Interactive):可交互时间

Google 搜索排名考量这些。性能差直接影响 SEO + 转化。

2. 采集

2.1 web-vitals 库

import { onCLS, onINP, onLCP, onFCP, onTTFB } from 'web-vitals'

function send(metric) {
navigator.sendBeacon('/api/rum', JSON.stringify({
name: metric.name,
value: metric.value,
id: metric.id,
rating: metric.rating, // 'good' | 'needs-improvement' | 'poor'
url: location.href,
ts: Date.now(),
}))
}

onLCP(send)
onINP(send)
onCLS(send)
onFCP(send)
onTTFB(send)

2.2 PerformanceObserver(原生)

new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(entry.name, entry.duration)
}
}).observe({ type: 'largest-contentful-paint', buffered: true })

3. RUM(Real User Monitoring)

真实用户数据。SaaS:

  • Sentry Performance
  • DataDog RUM
  • New Relic Browser
  • Cloudflare Web Analytics
  • 阿里 ARMS

自建:采集 → Kafka / Loki → Grafana。

4. Lighthouse / PageSpeed Insights

实验室数据(lab data),单次跑出来的:

npm i -g lighthouse
lighthouse https://example.com --view --preset=desktop

# CI 集成
npm i -g @lhci/cli
lhci autorun

.lighthouserc.json

{
"ci": {
"collect": { "url": ["https://staging.example.com"] },
"assert": {
"assertions": {
"categories:performance": ["error", { "minScore": 0.8 }],
"first-contentful-paint": ["warn", { "maxNumericValue": 2000 }]
}
}
}
}

5. 性能预算(Performance Budget)

CI 里强制:

// vite-bundle-analyzer / webpack-bundle-analyzer
// + size-limit
{
"size-limit": [
{
"path": "dist/index-*.js",
"limit": "200 KB"
},
{
"path": "dist/vendor-*.js",
"limit": "300 KB"
}
]
}
npx size-limit

PR 超预算 CI 失败,强制讨论。

6. 长任务监控

new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// 任务超过 50ms 即长任务
sendLongTask({
duration: entry.duration,
name: entry.name,
attribution: entry.attribution, // Chrome 才有
})
}
}).observe({ type: 'longtask', buffered: true })

INP 高的根因 = 长任务。

7. 资源加载监控

new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.duration > 1000) {
// 慢资源(> 1s)
sendSlowResource({
name: entry.name,
type: entry.initiatorType,
size: entry.transferSize,
duration: entry.duration,
})
}
}
}).observe({ type: 'resource', buffered: true })

8. 错误与性能联动

Sentry 把 LCP / INP 关联到 transaction,能看到"慢页面对应哪些组件渲染"。

9. 优化方向(按指标)

9.1 LCP 慢

  • 服务端 TTFB 慢 → SSR 优化、CDN
  • 阻塞渲染资源 → preload 关键 CSS、defer 非关键 JS
  • 大图 → WebP / AVIF + responsive
  • 字体 FOUT → font-display: swap + preload

9.2 INP 慢

  • 长任务太多 → 切片(requestIdleCallback、Web Worker)
  • React 大列表 → 虚拟滚动
  • 输入响应慢 → debounce / 节流

9.3 CLS 高

  • 图片 / 视频不设 width/height
  • 动态插入广告 / 横幅
  • 字体加载导致重排(用 size-adjust)

10. 监控告警

- alert: LCPDegraded
expr: histogram_quantile(0.75, sum(rate(rum_lcp_bucket[10m])) by (le, page)) > 3000
for: 10m
annotations:
summary: "页面 &#125;&#125; $labels.page &#125;&#125; P75 LCP > 3s"

按页面、设备类型、地域分维度看。

11. 常见反模式

  • 只看 lab 数据不看 RUM:lab 漂亮真实用户慢
  • 采样率 100%:数据量爆炸
  • 不分页面看:不同页面性能差异巨大,平均无意义
  • 不分 P50 / P75 / P95:均值掩盖长尾
  • 改了不验证:发版后不看新数据
  • CLS 只在桌面测:移动端 CLS 完全不同
  • 大图懒加载首屏:LCP 反而变慢(首屏图应该 eager + preload)

12. 延伸阅读