🎯 Web 性能 API 集合:Performance Observer 的 5 个冷门妙用

性能监控|浏览器 API | 你以为 Performance API 只是 performance.now()?PerformanceObserver 能在不阻塞主线程的前提下监听到 LCP、FMP、长任务、内存泄漏......甚至能捕获「谁在偷偷做耗时的同步操作」。

问题场景

线上项目偶尔出现卡顿,但在 DevTools 里抓不到复现。打点日志也看不出端倪------因为你只能看到结果 (页面慢了),看不到原因(谁占用了主线程)。

js 复制代码
// 传统的打点方式
const start = performance.now()
doHeavyWork()
console.log(`耗时:${performance.now() - start}ms`)

这种方式有两个致命缺陷:

  1. 侵入性强------要改造业务代码
  2. 抓不到偶发问题------你不知道什么时候该打点

解决方案:PerformanceObserver

PerformanceObserver 是一个观察者模式的性能 API,可以订阅多种性能事件,在事件发生时异步回调,零侵入。

1️⃣ 捕获所有长任务(Long Tasks)

哪些函数霸占了主线程超过 50ms?直接抓:

js 复制代码
const observer = new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    console.warn(`⚠️ 长任务 ${entry.duration.toFixed(0)}ms`, {
      name: entry.name,
      startTime: entry.startTime,
      duration: entry.duration,
      // attribution 包含具体函数信息(Chrome)
      culprit: entry.attribution?.[0]?.toJSON()
    })
  }
})

observer.observe({ type: 'longtask', buffered: true })

执行结果示例:

yaml 复制代码
⚠️ 长任务 312ms { name: "self", startTime: 4820.3, duration: 312.1, 
  culprit: { containerName: "window", containerId: "", containerSrc: "" } }

💡 配合 attribution 能定位到是哪个 iframe 或 Worker 触发的长任务。

2️⃣ 监控 LCP(最大内容绘制)

LCP 是 Core Web Vitals 核心指标,用 PerformanceObserver 可以精确知道哪个元素是 LCP 元素:

js 复制代码
const lcpObserver = new PerformanceObserver(list => {
  const entries = list.getEntries()
  const lastEntry = entries[entries.length - 1]
  console.log(`🎨 LCP 元素:${lastEntry.element || lastEntry.url}`, {
    renderTime: lastEntry.renderTime,
    loadTime: lastEntry.loadTime,
    size: lastEntry.size,
    id: lastEntry.id,
    url: lastEntry.url
  })
})

lcpObserver.observe({ type: 'largest-contentful-paint', buffered: true })

实战场景: 发现 LCP 元素是一张 background-image,排查发现图片没有 preload,加上 <link rel="preload"> 后 LCP 从 4.2s 降到 1.8s。

3️⃣ 自动追踪资源加载时序

想知道某个 API 请求为什么慢?不需要改 fetch 代码:

js 复制代码
const resourceObserver = new PerformanceObserver(list => {
  list.getEntries().forEach(entry => {
    if (entry.initiatorType === 'fetch' || entry.initiatorType === 'xmlhttprequest') {
      console.log(`🌐 ${entry.name}`, {
        dns: `${entry.domainLookupEnd - entry.domainLookupStart}ms`,
        tcp: `${entry.connectEnd - entry.connectStart}ms`,
        tls: `${entry.secureConnectionStart ? entry.connectEnd - entry.secureConnectionStart : 0}ms`,
        ttfb: `${entry.responseStart - entry.requestStart}ms`,
        download: `${entry.responseEnd - entry.responseStart}ms`,
        total: `${entry.duration}ms`
      })
    }
  })
})

resourceObserver.observe({ type: 'resource', buffered: true })

执行结果示例:

css 复制代码
🌐 https://api.example.com/users
  { dns: "2ms", tcp: "15ms", tls: "28ms", ttfb: "320ms", download: "45ms", total: "410ms" }

一眼看出瓶颈在 TTFB(服务端响应慢),不是网络问题。

4️⃣ 监控 First Paint / First Contentful Paint

js 复制代码
const paintObserver = new PerformanceObserver(list => {
  list.getEntries().forEach(entry => {
    console.log(`🎨 ${entry.name}: ${entry.startTime}ms`)
  })
})

paintObserver.observe({ type: 'paint', buffered: true })
// 🎨 first-paint: 435ms
// 🎨 first-contentful-paint: 435ms

5️⃣ 捕获 Layout Shift(CLS 指标)

js 复制代码
const clsObserver = new PerformanceObserver(list => {
  let cls = 0
  list.getEntries().forEach(entry => {
    if (!entry.hadRecentInput) {
      cls += entry.value
      console.warn(`💥 Layout Shift: ${entry.value.toFixed(3)}`, {
        source: entry.sources?.[0]?.node || 'unknown'
      })
    }
  })
  console.log(`📊 累计 CLS: ${cls.toFixed(3)}`)
})

clsObserver.observe({ type: 'layout-shift', buffered: true })

抓到某个广告位动态插入导致页面跳动,加上 min-height 占位后 CLS 从 0.35 降到 0.05。

要点总结

API 监控目标 关键字段
longtask 主线程卡顿 >50ms duration, attribution
largest-contentful-paint LCP 性能 element, renderTime
resource 资源/请求耗时 initiatorType, duration, 各阶段耗时
paint FP / FCP startTime
layout-shift CLS 布局偏移 value, sources

⚠️ 注意事项

  1. buffered: true 可以拿到注册之前的性能条目------这对首屏分析至关重要
  2. 用完记得 disconnect(),避免性能开销
  3. 并非所有浏览器都支持所有类型,生产环境加 try-catch
  4. 搭配 PerformanceServerTiming 可以拿到服务端下发的自定义指标

一句话总结:PerformanceObserver 是最优雅的「无侵入性能监控方案」------等线上用户反馈卡顿的时候,数据早就抓好了。

相关推荐
zach1 小时前
Vue/React SPA 打包部署后,子路由刷新 401 未登录问题彻底解决
前端·nginx·next.js
旋生万物1 小时前
素数螺旋映射 $z_n=n^{1+i}$ 的角分布统计检验与零模型对比
大数据·前端·人工智能·算法·云原生·螺旋生成论·螺旋相位
亿元程序员1 小时前
游戏引擎都没用!纯AI又上线了一款蚂蚁搬家小游戏!
前端
福兮说1 小时前
JS 正则的六个坑:带 g 的 test() 一真一假、空匹配死循环、replace 里的 $
开发语言·前端·javascript·正则表达式
码艺-Alimjan1 小时前
Web 网站打包桌面应用的另一种方式,超级简单(C# exe 33Kb)
开发语言·前端·c#
flash俊杰1 小时前
时间线到 filter_complex:把编辑文档编译成 FFmpeg 滤镜图
前端
Tyrvision2 小时前
新品发布前,三维动画怎么排期才不挤在最后一周?
前端
miss2 小时前
从零做一个可视化规则引擎:Vue3 递归条件树 + 双引擎结果对比
前端·vue.js·typescript
Daorigin_com2 小时前
道本科技携手DeepSeek:以AI重塑合同全生命周期管理
前端·人工智能·科技·网络安全·数据挖掘·前端框架·传媒
flash俊杰2 小时前
自动更新工程:electron-updater、差分更新、灰度发布与失败回滚
前端