前端监控笔记(二)

性能监控

反映页面加载耗时,通常结合 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               

类型

  1. navigation:Navigation Timing 监听。
  2. 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       // 从导航开始到该时间点的毫秒数
  1. 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)
  1. first-input:FID,反映主线程是否繁忙、是否有长任务阻塞事件响应。
typescript 复制代码
entry.startTime       // 用户第一次输入的时间
entry.processingStart // 浏览器开始处理这个事件的时间
entry.processingEnd   // 处理完成时间

// 核心计算
FID = entry.processingStart - entry.startTime  // 输入延迟,建议 < 100ms
  1. 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
  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' 通常是浏览器内部任务或无法追踪的来源。
  1. 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 // 解压后的响应体大小
  1. 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 的推送式监听策略来完善整体的性能指标监听。

参考内容

相关推荐
圣光SG2 小时前
面向对象编程(OOP)通用跨语言笔记
开发语言·笔记·oop
red_redemption2 小时前
自由学习记录(153)
学习
Shepherdppz2 小时前
【避坑指南】超级笔记 Supernote 私有云部署完整指南:从零到一在群晖Synology NAS上搭建私人同步服务器
运维·服务器·笔记
光影少年2 小时前
实现发布订阅模式
前端·javascript·设计模式
Jerry.张蒙2 小时前
大语言模型(LLM)的核心逻辑理解
大数据·人工智能·学习·语言模型·自然语言处理·区块链
猹叉叉(学习版)2 小时前
【系统分析师_知识点整理】 13.软件实现与测试
软件测试·笔记·软考·系统分析师
小陈phd2 小时前
多模态大模型学习笔记(二十九)—— 生成对抗网络(GAN)从原理到实战:实现第一个生成模型
笔记·学习·生成对抗网络
Heartache boy2 小时前
野火STM32_HAL库版课程笔记-TB6612FNG驱动有刷电机
笔记·stm32·单片机
iiiiii112 小时前
【理论推导】指数族分布的核心性质:对数配分函数的梯度为什么是充分统计量的期望?
人工智能·笔记·深度学习·数学·机器学习·概率论·指数族分布