登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  前端

前端 PerformanceObserver 怎样接收长任务条目

来源:17golang原创

时间:2026-10-20 02:52:51 182浏览 收藏

页面出现点击迟钝、滚动卡顿时,我会先确认主线程是否出现了长任务,而不是直接把所有慢都归因于网络。浏览器把连续占用主线程 50 毫秒或更久的工作记录为 longtask 条目,前端可以用 PerformanceObserver 订阅它们。

官方资料地址:https://developer.mozilla.org/en-US/docs/Web/API/PerformanceLongTaskTiming

最小配方是 new PerformanceObserver(callback) 配合 observer.observe({ type: 'longtask', buffered: true })。type 指定单一条目类型,buffered 让观察器接收建立之前已经被浏览器缓冲的条目;回调里再通过 list.getEntries() 批量处理。
要点速览
  • longtask 主要反映主线程连续忙碌达到 50ms 的任务,不能等同于完整的页面交互耗时。
  • 使用 buffered: true 时要读取回调的第三个参数,留意 droppedEntriesCount。
  • 生产代码应先检查 PerformanceObserver.supportedEntryTypes,并在采集结束时调用 disconnect()。

先确认 longtask 的观测边界

长任务是一个连续的主线程忙碌区间,常见来源包括很重的事件处理、昂贵的重排或重绘,以及一段没有主动让出主线程的 JavaScript。它适合回答“页面是否被一段连续工作卡住”,但不能直接告诉你某个函数就是唯一根因,也不能覆盖所有慢交互。

PerformanceLongTaskTiming 继承自 PerformanceEntry,常用字段是 startTime、duration、name 和 attribution。其中 duration 是条目持续时间,attribution 可能提供任务所在容器的信息。浏览器兼容性需要单独查看,不能因为 PerformanceObserver 本身普遍可用,就假定 longtask 在所有环境都存在。

主线程长任务、PerformanceObserver 和 PerformanceLongTaskTiming 的静态关系说明图
图1:结构说明图,查看主线程长任务、观察器和性能条目之间的关系;这是原创静态说明图,不是浏览器截图或运行证据。

用 type 与 buffered 建立观察器

observe() 有两种容易混淆的配置形态:entryTypes 用来观察多个类型,而 type 用来观察一个类型并配合 buffered。本题只接收 longtask,因此使用 type 更清楚,也避免把 buffered 和 entryTypes 放在一起。

const supported = PerformanceObserver.supportedEntryTypes ?? [];

if (supported.includes('longtask')) {
  const observer = new PerformanceObserver((list, _observer, options) => {
    // 一次回调可能包含多个条目,先批量取出再统一处理。
    for (const entry of list.getEntries()) {
      console.log({
        type: entry.entryType,
        duration: entry.duration,
        startTime: entry.startTime,
        name: entry.name,
      });
    }

    // 缓冲区满时,记录丢失数量,避免把采样结果误认为完整数据。
    if (options?.droppedEntriesCount > 0) {
      console.warn('longtask 条目有丢失:', options.droppedEntriesCount);
    }
  });

  // buffered 会把观察建立前已缓冲的 longtask 也交给回调。
  observer.observe({ type: 'longtask', buffered: true });

  // 页面或组件结束采集时调用 disconnect,停止继续接收条目。
  window.addEventListener('pagehide', () => observer.disconnect(), { once: true });
}

这里的支持检测是一个分支,而不是对性能结果的判定:不支持时表示当前环境不能通过这条 API 获得数据,并不表示页面没有长任务。buffered 也不是“永久保存”,浏览器仍有缓冲容量限制。

从条目列表提取数据并控制上报量

观察器回调收到的是 PerformanceObserverEntryList,更适合在回调中完成轻量的归一化,然后把小批量结果交给上报队列。不要在每个条目回调里立即发一个网络请求,否则采集本身可能增加主线程和网络压力。

const pending = [];

function collectLongTasks(list) {
  // 只保留上报需要的字段,避免把整条对象直接序列化。
  for (const entry of list.getEntries()) {
    pending.push({
      duration: Math.round(entry.duration),
      startTime: Math.round(entry.startTime),
      container: entry.name,
      attribution: entry.attribution?.map((item) => ({
        // 归因字段可能随浏览器实现变化,缺失时保留 undefined。
        containerId: item.containerId,
        containerName: item.containerName,
        containerSrc: item.containerSrc,
      })),
    });
  }
}

const observer = new PerformanceObserver((list) => collectLongTasks(list));
observer.observe({ type: 'longtask', buffered: true });

function takeBatch() {
  // 复制后清空本地队列,让下一轮采集不重复发送旧条目。
  const batch = pending.splice(0, pending.length);
  return batch;
}

attribution 适合做辅助线索,不能把它当作精确的函数调用栈。上报前还要按产品的隐私规则处理 containerSrc 等字段,并设置队列上限;本文只讨论如何接收条目,不把采集数据当作已完成的线上诊断。

longtask 条目列表、归一化字段、批量队列和上报边界的静态关系图
图2:结构说明图,查看条目列表到归一化队列的静态关系和数据边界;这是原创静态说明图,不是截图或运行结果。

调试检查清单:不生效时逐层排查

如果回调没有输出,我会按下面顺序排除,而不是先改阈值:

  • 先看支持项:检查 PerformanceObserver.supportedEntryTypes 是否包含 longtask;不支持时记录能力差异,不能用空结果下结论。
  • 再看订阅形态:确认使用的是 type: 'longtask',而不是把单类型写进 entryTypes 后又期待 buffered 生效。
  • 再看时机:观察器越早建立,越容易接住后续条目;buffered 只能回放浏览器实际保留的条目。
  • 最后看采集代码:确认没有过早调用 disconnect(),也不要在回调里执行重计算或同步大批量序列化。

还要留意 API 的边界:长任务以 50ms 为门槛,连续多个低于门槛的工作可能不会合并成一个 longtask;需要更细的渲染与脚本归因时,应另行评估 Long Animation Frames API,而不是把本文方案无限扩展。

常见问题

为什么 performance.getEntriesByType('longtask') 取不到数据?

部分条目类型不能通过通用的 getEntriesByType() 读取,长任务应使用 PerformanceObserver 订阅。

buffered: true 是不是能拿到页面从打开以来的全部长任务?

不是。它请求浏览器把已缓冲的匹配条目交给观察器,仍受浏览器缓冲容量和支持范围影响;回调中的 droppedEntriesCount 可以帮助发现缓冲丢失。

观察器应该什么时候断开?

组件销毁、页面离开或采集阶段结束时断开。持续监控则应保留观察器,但要让队列有上限,并把处理逻辑保持得足够轻。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>