用户体验度量-RUM
1. RUM vs 实验室
| 类型 | 数据来源 | 优点 | 缺点 |
|---|---|---|---|
| Lab(Lighthouse / WebPageTest) | 固定环境跑 | 可重复、可对比 | 不代表真实用户 |
| RUM(Real User Monitoring) | 真实用户上报 | 真实数据 | 噪音多、数据量大 |
生产必须两者结合。
2. 核心指标
2.1 Core Web Vitals
- LCP(最大内容绘制)< 2.5s
- INP(交互响应)< 200ms
- CLS(累积布局偏移)< 0.1
2.2 辅助
- TTFB(首字节)< 800ms
- FCP(首次内容绘制)< 1.8s
- TTI(可交互时间)
2.3 业务指标
- 关键页面加载时长
- 关键操作响应时长(搜索、下单)
- 错误率 / JS Error
- API 失败率
3. 采集
3.1 web-vitals
import { onCLS, onINP, onLCP, onFCP, onTTFB } from 'web-vitals'
function send(metric) {
const body = JSON.stringify({
name: metric.name,
value: metric.value,
id: metric.id,
rating: metric.rating, // good / needs-improvement / poor
delta: metric.delta,
navigationType: metric.navigationType,
url: location.href,
referrer: document.referrer,
ua: navigator.userAgent,
connection: navigator.connection?.effectiveType,
deviceMemory: navigator.deviceMemory,
ts: Date.now(),
})
if (navigator.sendBeacon) {
navigator.sendBeacon('/api/rum', body)
} else {
fetch('/api/rum', { body, method: 'POST', keepalive: true })
}
}
onLCP(send)
onINP(send)
onCLS(send)
onFCP(send)
onTTFB(send)
3.2 sendBeacon
页面切换时 fetch 可能被中断,sendBeacon 异步保证发送。
3.3 Resource Timing
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.duration > 1000) {
sendSlowResource({
name: entry.name,
type: entry.initiatorType,
size: entry.transferSize,
duration: entry.duration,
})
}
}
}).observe({ type: 'resource', buffered: true })
3.4 长任务
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
sendLongTask({
duration: entry.duration,
startTime: entry.startTime,
})
}
}).observe({ type: 'longtask', buffered: true })
4. 采样
100% 上报数据量爆炸。常见策略:
- 全采 Core Web Vitals(每页一次)
- 10% 采资源加载
- 1% 采详细 timing
- 错误 100% 采
const SAMPLE_RATE = 0.1
if (Math.random() < SAMPLE_RATE) {
reportDetailedTiming()
}
按用户分组:
const userId = getUserId()
const sample = parseInt(userId.slice(-2), 16) / 255 < 0.1
// 同一用户始终采样或始终不采
5. 维度分组
不能只看平均,要分维度:
- 页面:首页 vs 详情 vs 搜索
- 设备:移动 vs 桌面
- 网络:4g / 3g / wifi
- 地区:国内 vs 海外
- 用户类型:新 vs 老
- 版本:v1.0 vs v1.1
- 浏览器:Chrome / Safari / 微信内置
按维度看 P50 / P75 / P95 / P99,长尾用户也要照顾。
6. 后端接收
// Express
app.post('/api/rum', express.json({ limit: '10kb' }), (req, res) => {
const data = req.body
// 异步写 Kafka / 直接写 Prometheus / 转发 Loki
metricsQueue.send(data)
res.status(204).end()
})
或直接写时序数据库(InfluxDB / Prometheus pushgateway)。
7. 可视化
Grafana 看板:
- LCP / INP / CLS 趋势(P75)
- 按页面 / 设备 / 地区拆分
- 与发版关联(看版本前后变化)
- 错误率叠加
8. SaaS 方案
| 工具 | 特点 |
|---|---|
| Sentry Performance | 错误 + 性能一站式 |
| Datadog RUM | 全面,贵 |
| New Relic Browser | 老牌 |
| Cloudflare Web Analytics | 免费简洁 |
| Vercel Analytics | Vercel 用户友好 |
| 阿里 ARMS / 字节 Slardar | 国内 |
9. 实战:发现并解决慢页面
1. RUM 看板发现首页 P75 LCP 4s(差)
2. 拆分发现移动端 + 4G 用户最慢
3. Resource Timing:hero 图 800ms
4. 优化:WebP + responsive + preload
5. 部署后 RUM 数据 P75 LCP 降到 2s
10. 隐私合规
- 不采集 PII(手机号、身份证)
- IP 脱敏
- 遵守 GDPR / CCPA(用户同意 / 不追踪选项)
- Cookie 同意横幅
11. 常见反模式
- 只看 P50:长尾用户看不见
- lab 漂亮但 RUM 差:现实和实验环境差距大
- 不分维度:移动端慢被桌面平均
- 采样率 100%:数据量爆 / 后端打挂
- 没有发版关联:上线后变慢不知是哪个版本
- 上报阻塞主线程:用 sendBeacon 异步
- 不限制后端接口请求量:被恶意刷
- 采集 PII:合规风险
12. 延伸阅读
- web-vitals
- web.dev: Measure performance with the RUM API
- CrUX (Chrome User Experience Report)
- 模块 08 性能监控与 Web Vitals