后端再健康,用户看到的页面白屏、点击没反应,体验依然是零。前端与移动端 RUM(Real User Monitoring) 采集"真实用户在真实设备上的真实体验":加载快不快、交互顺不顺、布局稳不稳、报了什么错。本指南从 Web Vitals 讲起,覆盖 Performance API 采集、长任务与错误还原、会话与采样、移动端 App 监控,以及如何把前端指标与后端 trace 关联,定位"到底哪一层慢"。
关键概念:RUM 采集"真实用户"的前端性能与错误数据。Web Vitals 是标准化体验指标:LCP(加载)、INP(交互)、CLS(稳定)。会话(Session)把一次访问内的多次事件聚合成一条可回放的故事线。
- 1. Web Vitals 三大核心指标:LCP、INP 与 CLS
- 2. RUM 采集原理:Performance API 与导航计时
- 3. 资源计时与长任务:性能下钻
- 4. 错误采集与 Source Map 还原
- 5. 会话、采样与隐私合规
- 6. 移动端 App 监控与后端指标关联
- 7. 常见避坑
- 8. 最佳实践清单
1. Web Vitals 三大核心指标:LCP、INP 与 CLS
1.1 指标含义与目标值
LCP(Largest Contentful Paint):最大内容渲染完成时间
良好 <= 2.5s / 需改进 <= 4s / 差 > 4s
INP(Interaction to Next Paint):交互里"最差"的响应延迟
良好 <= 200ms / 需改进 <= 500ms / 差 > 500ms
CLS(Cumulative Layout Shift):布局位移累积分
良好 <= 0.1 / 需改进 <= 0.25 / 差 > 0.25
意义:这些指标被纳入搜索引擎排名,直接影响商业流量
1.2 为什么是"用户体验"指标与诊断维度
相比 DOMContentLoaded/页面加载完成:
- 那些指标"页面加载完了"就结束,不看用户感知
- Web Vitals 看"用户真正看到、操作时的体验",对弱网做了优先级设计
诊断拆两个维度:
- 分位数 P75/P95,别只看平均(慢尾用户才是差体验)
- 按浏览器/设备/地域/网络/页面 URL 分组
示例:全站 LCP P75 = 2.3s(良好),移动端 4G P95 = 6.8s → 问题在弱网
2. RUM 采集原理:Performance API 与导航计时
2.1 导航计时(Navigation Timing)
// 阶段:DNS→TCP/TLS→TTFB→下载→解析→渲染,看哪层最慢
const nav = performance.getEntriesByType('navigation')[0];
sendRum('load', {
ttfb: nav.responseStart - nav.requestStart, // 首字节
total: nav.loadEventEnd - nav.fetchStart,
url: location.pathname, device: detectDevice() });
2.2 PerformanceObserver 持续监听
new PerformanceObserver((l) => l.getEntries().forEach(e =>
e.entryType === 'largest-contentful-paint' &&
sendRum('lcp', { value: e.startTime })))
.observe({ type: 'largest-contentful-paint', buffered: true });
// INP 监听交互延迟;CLS 监听 layout-shift
2.3 上报策略
LCP/CLS 在卸载前用 sendBeacon 保底;会话数据 idle 时批量上报
采样:大流量按会话哈希采样(5%~10%);错误全量(量小价值高)
3. 资源计时与长任务:性能下钻
3.1 资源计时(Resource Timing)
// 找出拖慢 LCP 的大图/阻塞脚本;按 CDN 域名聚合看节点质量
performance.getEntriesByType('resource')
.filter(r => r.duration > 500)
.forEach(r => sendRum('resource', {
name: r.name, duration: r.duration,
domain: new URL(r.name).hostname }));
3.2 长任务(Long Tasks)
Long Task = 主线程阻塞 > 50ms 的同步任务(直接恶化 INP)
- 用 PerformanceObserver('longtask') 捕获
来源分析:大段 JS 执行、布局重排、第三方脚本阻塞主线程
优化联动:拆任务、用 rIC/调度、懒加载第三方
4. 错误采集与 Source Map 还原
4.1 JS 错误与未捕获异常
window.addEventListener('error', (e) => sendRum('error', {
message: e.message, stack: e.error?.stack, lineno: e.lineno,
url: location.href, userAgent: navigator.userAgent }));
window.addEventListener('unhandledrejection',
(e) => sendRum('error', { reason: String(e.reason) }));
// 跨域脚本需 <script crossorigin> + CORS,否则错误变 "Script error." 无堆栈
4.2 Source Map 还原压缩代码
压缩后的堆栈全是 a.b.c 映射,必须还原:
1. 构建时生成 sourcemap 上传 RUM 平台(私有存储)
2. 用 stacktrace.js 解析,还原出 源码文件:行号:列号
安全注意:sourcemap 含源码,切勿公开部署到 CDN
价值:把 "TypeError: undefined" 从第 1 行还原成 checkout.js:412
4.3 错误分组与去重
海量相同错误(如浏览器插件)会淹没真实问题:
- 按"文件+行号+message"分组,统计受影响用户数
- 新错误(regression)与存量错误分开标记
发布对比:发布前无该错误 → 发布后爆发 = 明确回归,直接回滚
5. 会话、采样与隐私合规
5.1 会话与用户标识
会话:session_id(30 分钟无活动切新)+ 事件序列可回放
标识用户:未登录用 anonymous id;登录后映射业务用户(脱敏)
红线:严禁直接采集身份证/手机号等 PII
5.2 采样、成本与隐私合规
采样策略(大流量站点必须):
- 全量 5%~10% 会话;错误/崩溃全量;回放最贵仅 1%~5%
- 保留:性能 30 天、错误 90 天、回放 7 天
隐私合规:默认不采集输入框内容;敏感字段打码;
用户可退出(opt-out);遵守 GDPR/个保法
红线:密码框、银行卡号、聊天内容永不采集;回放屏蔽敏感页面
6. 移动端 App 监控与后端指标关联
6.1 移动端 App 的 RUM
与 Web 的差异:无 DOM,指标是原生渲染/网络/启动时间
App 关键指标:
- Launch Time:冷启动到首帧(App 的"LCP")
- ANR(Android):无响应时长;崩溃率 > 0.1% 需关注
- 网络:HTTP 时延按运营商/地域/网络类型分组
采集方式:SDK 埋点(Firebase Performance / 自建 SDK)
6.2 前后端指标关联:前端慢到底怪谁
关键手段:前端事件带 trace_id
前端请求 → 后端 Span 记录 trace_id
→ 前端慢请求携带 trace_id → 后端按 trace_id 检索
定位链条:LCP 慢 → 查 TTFB/资源下载 → TTFB 慢则带 trace_id
进后端 trace,哪个 Span 慢(网关/DB/第三方)一目了然
没有 trace_id:前后端互相甩锅
6.3 统一面板与告警
前端指标进统一告警:
- LCP P95 超基线 2 倍持续 10 分钟 → 页面性能告警
- JS 错误率突增(> 1%)→ 前端回归告警
- 崩溃率超阈值 → App 版本质量告警
闭环:性能告警 → 关联发布事件 → 定位变更 → 回滚/优化
7. 常见避坑
| 坑 | 现象 | 对策 |
|---|---|---|
| 只看平均值 | 慢尾用户被掩盖 | 用 P75/P95 分位数 |
| 跨域脚本错误无堆栈 | 全是 Script error. | crossorigin + CORS |
| sourcemap 公开部署 | 源码泄露 | 只上传 RUM 平台私有存储 |
| 卸载时用 fetch 上报 | 数据大量丢失 | navigator.sendBeacon |
| 回放全量采集 | 成本爆炸 | 回放低采样、敏感页面屏蔽 |
| 采集输入框内容 | 合规风险 | 默认屏蔽,打码处理 |
| 前端慢怪后端/互相甩锅 | 定位难 | 前端事件带 trace_id 关联 |
| 无分组去重 | 海量相同错误刷屏 | 按文件+行号+message 分组 |
8. 最佳实践清单
□ LCP/INP/CLS 用 PerformanceObserver 采集,P75/P95 分位数统计
□ 导航计时拆阶段,资源计时找阻塞 LCP 的大图/脚本
□ 长任务监听主线程阻塞,优化 INP
□ 错误带堆栈,sourcemap 私有还原,按文件+行号分组去重
□ 会话回放低采样,敏感输入永不采集,遵守 GDPR/个保法
□ 大流量按会话哈希采样,错误全量
□ 移动端看启动时间/崩溃率/ANR/帧率
□ 前端事件带 trace_id,与后端 trace 关联定位
□ 前端指标进统一告警,联动发布事件
一句话原则
RUM = 真实体验指标(LCP/INP/CLS)+ 错误还原 + 采样合规 +
前后端 trace 关联,把"用户感受"变成"可定位的数据"。
小结
前端与移动端 RUM 的核心是"采集真实体验、还原错误、关联后端":用 PerformanceObserver 采集 Web Vitals(LCP/INP/CLS) 并分位数、分设备网络统计;用导航/资源计时定位加载瓶颈、长任务定位交互卡顿;用 Source Map 还原压缩代码错误;通过会话回放与分层采样控制成本、遵守隐私合规;移动端则看启动时间与崩溃率;最后让前端事件携带 trace_id 与后端 trace 关联。落地记住五件事:分位数统计、sendBeacon 上报、sourcemap 私有化、采样分层、trace_id 关联。当用户的每一次"卡、白屏、报错"都能还原成一条可定位的链路时,前端可观测性才真正成为体验工程的引擎。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。