🎯 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 是最优雅的「无侵入性能监控方案」------等线上用户反馈卡顿的时候,数据早就抓好了。

相关推荐
mldong31 分钟前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排1 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60612 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角3 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!3 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰3 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi5 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen5 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马6 小时前
AI源码分析:拆解模型底层实现逻辑
前端
IT_陈寒6 小时前
React子组件莫名其妙重渲染?你可能漏了这个Hook
前端·人工智能·后端