核心是用 PerformanceObserver 监听 paint 和 navigation 类型指标,首屏性能捕获需在 head 中尽早初始化;RUM 的 TTFB 需排除服务端渲染与协议干扰;LongTask 比 FPS 更可靠反映卡顿;卸载时用 sendBeacon 发送监控数据。

如何在 HTML5 应用上线后实时捕获首屏加载性能
核心是利用 PerformanceObserver 监听 navigation 和 paint 类型指标,而非依赖 onload 或 DOMContentLoaded——后者无法反映真实用户感知的“内容可见”时间。
-
navigation提供loadEventStart、domComplete等完整导航生命周期,但注意单页应用(SPA)中后续路由跳转不会触发新 navigation 记录 -
paint可捕获first-paint和first-contentful-paint(FCP),FCP 是 Lighthouse 和 CrUX 的关键指标,必须用PerformanceObserver订阅,performance.getEntriesByType('paint')在页面后期调用可能漏掉 - 避免在
DOMContentLoaded后才初始化 observer,应放在内尽早执行,否则错过首屏关键事件
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.name === 'first-contentful-paint') {
sendToMonitoring({ metric: 'FCP', value: entry.startTime });
}
}
});
observer.observe({ entryTypes: ['paint'] });怎么区分真实用户(RUM)和实验室测试(Lighthouse)的 TTFB 差异
TTFB(Time to First Byte)在 RUM 中不能直接取 performance.getEntriesByType('navigation')[0].responseStart - performance.getEntriesByType('navigation')[0].requestStart 就完事——CDN 缓存、HTTP/2 多路复用、服务端渲染(SSR)或边缘计算都会让这个差值失真。
- 若页面走 SSR 或静态生成,
requestStart到responseStart实际包含服务端模板渲染耗时,不是纯网络延迟 - 使用 HTTP/2 或 QUIC 时,多个资源共用连接,
requestStart可能被浏览器合并或延迟调度,导致 TTFB 偏高 - 建议同时采集
connectStart和secureConnectionStart,判断是否卡在 TLS 握手(尤其 iOS WebKit 对 OCSP stapling 敏感)
const nav = performance.getEntriesByType('navigation')[0];
const ttbfRaw = nav.responseStart - nav.requestStart;
const tlsDelay = nav.secureConnectionStart > 0
? nav.connectEnd - nav.secureConnectionStart
: 0;为什么用 LongTask 监控卡顿比 FPS 更可靠
FPS 是推算值,依赖 requestAnimationFrame 回调节拍,而主线程被阻塞时回调根本不会触发,造成“假高帧率”。LongTask 是浏览器原生暴露的 >50ms 的任务记录,直接反映 JS 执行、样式计算、布局等阻塞行为。
- 只监听
longtask类型即可,无需自行 diff 时间戳;每个entry.duration就是实际阻塞时长 - 注意:iOS Safari 直到 iOS 17.4 才支持
LongTask,旧版本需回退到event loop delay采样(如每 100ms postMessage 检查时间差) - 单次
duration > 100ms就可能引发明显掉帧,建议按 50ms / 100ms / 200ms 分档上报,便于定位是偶发 GC 还是持续轮询
const lo = new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
if (entry.duration > 100) {
sendToMonitoring({
metric: 'LongTask',
duration: entry.duration,
attribution: entry.attribution
});
}
});
});
lo.observe({ entryTypes: ['longtask'] });前端监控数据发往后端时,如何避免埋点请求拖慢页面卸载
用 navigator.sendBeacon() 是唯一稳妥方案。XMLHttpRequest 或 fetch 在 beforeunload 中发起请求,浏览器大概率会中断,尤其在 iOS Safari 和 Chrome 移动端。
立即学习“前端免费学习笔记(深入)”;
-
sendBeacon()是异步且不可取消的,即使页面已销毁也会发出请求,但仅支持POST且 payload 必须是ArrayBuffer、Blob或FormData - 不要在
visibilitychange隐藏时立刻发 beacon,应加 300ms 延迟并检查document.visibilityState === 'hidden',避免误报后台标签页切换 - 若后端接口不支持二进制接收,需提前将 JSON 序列化为
Blob,不能传字符串
const data = new Blob([JSON.stringify(performanceMetrics)], {
type: 'application/json'
});
navigator.sendBeacon('/log', data);真实场景里最容易被忽略的是:单页应用中 PerformanceNavigationTiming 不会随路由更新,而开发者常误以为它能反映每次页面切换的性能。必须结合 History API 监听 + 手动打点,或者改用 Navigation Timing Level 2 的 navigation observer 并启用 buffered: true。











