性能监控
页面加载检测 Performance Navigation Timing
反映页面加载耗时,通常结合 P50、P75、P90 数据进行分析。

- DNS 统计 :domainLookupEnd - domainLookupStart,分析 DNS 耗时统计。
- 正常解析时间在 0-30ms。
- 如果持续偏高后又降低,说明内部缓存生效。
- 优化:
rel="dns-prefetch"配置,减少域名数量,更换更快 DNS 服务商。
- 不带 SSL 的连接 :connectEnd - connectStart,比对连接。
- 分析 TCP 连接耗时,如果为 0 说明命中连接复用 keep-alive。
- 如果偏高反映 RTT 高。
- 优化:HTTP/2 多路复用,CDN,keep-alive 连接复用,
rel="preconnect"提前建立连接。
- 带 SSL :connectEnd - secureConnectionStart,比对 SSL 的开销。
- 衡量 TLS 握手耗时,正常 50-200ms。
- 过高说明证书链过长,加密算法过重或者握手往返次数多。
- 优化:开启 TLS 1.3,把握手 2 RTT 降到 1 RTT,开启 Session Ticket 复用 TLS 会话,优化证书链,移除多余中间证书。
- TTFB 首字节时间 :responseStart - requestStart,反映请求开始到收到响应,反映服务端的响应速度。
- 反映服务端或网络质量,建议小于800ms。
- 如果 DNS 和 TCP 连接都正常则是服务端的问题,如果 DNS、TCP 连接都异常,则是网络质量问题。
- 优化方案:服务端优化接口响应速度,Redis 缓存;CDN 缓存响应,数据库索引,SSR 或者边缘计算。
- download 响应时间 :responseEnd - responseStart。
- 反映资源体积大小。
- 如果时间过长,需要考虑优化资源体积。
- 优化:gzip / brotli 压缩,接口分页,图片使用 WebP 等格式,静态资源通过 CDN 请求。
- domParse DOM 获取到解析完成时间 :domInteractive - responseEnd,从得到响应到可交互的时间。
- 反映收到 HTML 到 DOM 解析的时间。
- 如果过高说明 HTML 过大,或者有同步 JS 阻塞线程。
- 优化:JS
defer/async避免阻塞 HTML,减少 DOM 节点,SSR 流式渲染,非关键 CSS 异步加载。
- domReady DOM 准备完成时间 :domContentLoadedEventEnd - navigationStart,从导航开始到 DOM 加载完成这段时间。
- DOMContentLoaded 触发时间,前端可交互的时间节点。
- 是前面内容的时间综合。
- 优化:减少渲染内容,代码分割,懒加载。
- loadTime :loadEventEnd - navigationStart,统计整体加载时间。
- 完整加载时间。
- 如果比 domReady 高很多,说明有大量异步资源。
- 优化:图片懒加载,动态注入延迟加载埋点等脚本,
preload加载关键资源,字体可以设置font-display: swap避免阻塞。
typescript
const [nav] = performance.getEntriesByType('navigation')
// 单位是毫秒
const metrics = {
dns: nav.domainLookupEnd - nav.domainLookupStart,
tcp: nav.connectEnd - nav.connectStart,
ssl: nav.connectEnd - nav.secureConnectionStart, // HTTPS 才有
ttfb: nav.responseStart - nav.requestStart, // 首字节时间
download: nav.responseEnd - nav.responseStart,
domParse: nav.domInteractive - nav.responseEnd,
domReady: nav.domContentLoadedEventEnd - nav.navigationStart,
loadTime: nav.loadEventEnd - nav.navigationStart, // navigationStart 换成 startTime 或 0
}
PerformanceObserver API
通过监听等待主动推送(Push 模式),只要触发就会监听,定义 buffered: true,可以拿到注册之前的性能数据。
Web Vitals 监听设计:
LCP:获取 largest-contentful-paint 最后一条的 startTime。
FID:监听 first-input,entry.processingStart - entry.startTime 统计 FID。
CLS:监听 layout-shift,总和累加 entry.value,需要通过 hadRecentInput: false 只统计非用户引起的布局偏移。
TTFB:使用 Navigation Timing 统计 metrics。
FCP:监听 paint,通过 name 区分 FP 和 FCP,然后按 startTime 统计。
typescript
const observer = new PerformanceObserver((list) => {
// 捕获获取
for (const entry of list.getEntries()) {
if (entry.entryType === 'largest-contentful-paint') {
console.log('LCP:', entry.startTime)
}
}
})
// 可观测的 entryType 列表
observer.observe({ type: 'navigation', buffered: true })
observer.observe({ type: 'resource', buffered: true })
observer.observe({ type: 'largest-contentful-paint', buffered: true })
observer.observe({ type: 'first-input', buffered: true }) // FID
observer.observe({ type: 'layout-shift', buffered: true }) // CLS
observer.observe({ type: 'longtask', buffered: true }) // 超过 50ms 的任务
observer.observe({ type: 'paint', buffered: true }) // FP / FCP
类型:
- navigation:Navigation Timing 监听。
- paint :FP / FCP 监听,首个有意义的内容,
name获取类型,startTime获取从导航开始的毫秒数(FCP < 1.8 s)。
typescript
observer.observe({ type: 'paint', buffered: true })
entry.name // 'first-paint' | 'first-contentful-paint'
entry.startTime // 从导航开始到该时间点的毫秒数
- largest-contentful-paint :LCP,最大元素,会多次触发更新,只取最后一次(LCP < 2.5 s)。
- 可以通过 url 分析是哪个图片拖慢加载。
- 通过 element 分析是哪个元素拖慢渲染。
typescript
entry.startTime // LCP 发生时间,建议 < 2.5s
entry.size // 元素面积(px²),越大越可能是 LCP 候选
entry.element // 触发 LCP 的 DOM 元素,定位是哪个元素拖慢了渲染
entry.url // 如果是图片资源,这里是图片 URL,否则为空
entry.loadTime // 资源加载完成时间
entry.renderTime // 元素实际渲染时间(跨域图片可能为 0)
- first-input:FID,反映主线程是否繁忙、是否有长任务阻塞事件响应。
typescript
entry.startTime // 用户第一次输入的时间
entry.processingStart // 浏览器开始处理这个事件的时间
entry.processingEnd // 处理完成时间
// 核心计算
FID = entry.processingStart - entry.startTime // 输入延迟,建议 < 100ms
- layout-shift:CLS,布局偏移量,统计布局偏移总和进行计算。
typescript
entry.value // 本次位移分数(0~1)
entry.hadRecentInput // 是否因用户交互触发(是的话不计入 CLS)
entry.sources // 位移的元素列表
└── source.node // 具体哪个 DOM 元素发生了位移
└── source.previousRect / currentRect // 位移前后的位置
// 核心计算:累加所有 hadRecentInput=false 的 entry.value
CLS = sum(entry.value) // 建议 < 0.1
- longtask:长任务统计,会影响 FID 和页面响应,结合 startTime 定位卡顿时间。
typescript
// PerformanceLongTaskTiming
entry.startTime // 任务开始时间
entry.duration // 任务耗时(必然 > 50ms)
entry.attribution // 任务来源信息
└── [0].name // 'self' | 'same-origin-ancestor' 等
└── [0].containerType // 'iframe' | 'embed' | 'object' | 'window'
└── [0].containerSrc // 如果是 iframe,是其 src
// name
// 'self' 长任务发生在当前页面自己的主线程里。最常见的情况,说明是你自己的代码(或你加载的脚本)造成了阻塞。
// 'same-origin-ancestor' 当前页面是一个 iframe,长任务发生在它的父页面里,且父页面和 iframe 同域。
// 'same-origin-descendant' 长任务发生在当前页面嵌套的某个 iframe 里,且那个 iframe 和当前页面同域。
// 'same-origin' 同源但无法确定是祖先还是子孙关系。
// 'cross-origin-ancestor' 父页面是跨域的,长任务来自父页面,但出于安全限制,具体信息会被隐藏。
// 'cross-origin-descendant' 嵌套的 iframe 是跨域的,长任务来自那个 iframe。这种情况下 containerSrc 等信息也会被屏蔽。
// 'unknown' 通常是浏览器内部任务或无法追踪的来源。
- resource:监听资源加载耗时和资源类型,可以分析传输数据大小、是否命中缓存、响应时长、TTFB、解压和压缩前后大小。
typescript
entry.name // 资源 URL
entry.initiatorType // 'script' | 'img' | 'css' | 'fetch' | 'xmlhttprequest' ...
entry.duration // 资源总加载耗时
// 细分阶段(和 Navigation Timing 一致)
entry.domainLookupEnd - entry.domainLookupStart // DNS
entry.connectEnd - entry.connectStart // TCP
entry.responseStart - entry.requestStart // TTFB
entry.responseEnd - entry.responseStart // Download
entry.transferSize // 实际传输大小(含请求头),为 0 说明命中缓存
entry.encodedBodySize // 压缩后的响应体大小
entry.decodedBodySize // 解压后的响应体大小
- event :监听事件交互,可以统计交互耗时、输入延迟、渲染延迟,可用于替代 FID。默认只有事件耗时超过 104ms 才会被监听,需要配置
durationThreshold(通用配置)来降低阈值。104ms 是推荐的关注临界点(INP 推荐 < 200ms),避免频繁触发造成性能开销。
typescript
entry.name // 事件名:'click' | 'keydown' | 'pointerdown' | 'input' ...
entry.startTime // 事件发生时间(用户实际交互的时间点)
entry.processingStart // 浏览器开始执行事件回调的时间
entry.processingEnd // 事件回调执行完毕的时间
entry.duration // 从事件发生到下一帧绘制的总耗时(以 8ms 对齐取整)
entry.cancelable // 事件是否可被 preventDefault
输入延迟 = processingStart - startTime // 主线程繁忙导致事件排队等待时长
处理耗时 = processingEnd - processingStart //事件回调执行时长
渲染延迟 = duration - (processingEnd - startTime) // 回调完成到下一帧的等待时长
总结
- Navigation Timing:基于 Navigation Timing 拆解页面加载过程,针对从 DNS 到 页面加载阶段进行优化。
- PerformanceObserver :通过 PerformanceObserver 采集 FCP、LCP、CLS、FID 指标,,结合
buffered: true的推送式监听策略来完善整体的性能指标监听。