如何统计页面的long task(长任务)

426 阅读3分钟

在前端性能优化中,统计页面的 Long Task(长任务) 是识别潜在性能瓶颈的关键步骤。长任务指执行时间超过 50ms 的任务,这类任务会阻塞主线程,导致页面响应延迟、动画卡顿等问题。以下是几种主流的统计方法及其实现:

一、使用Performance API(推荐方案)

原理:通过 PerformanceObserver 监听 longtask 条目,直接捕获浏览器检测到的长任务。

// 初始化长任务收集器
function initLongTaskObserver() {
  const longTasks = [];
  
  // 创建性能观察器
  const observer = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      longTasks.push({
        duration: entry.duration,  // 任务持续时间(毫秒)
        startTime: entry.startTime,  // 任务开始时间
        attribution: entry.attribution,  // 任务归属信息(如iframe、脚本)
      });
    }
  });
  
  // 开始观察长任务
  observer.observe({ entryTypes: ['longtask'] });
  
  // 返回收集结果(可在适当时候调用)
  return () => longTasks;
}

// 使用示例
const getLongTasks = initLongTaskObserver();

// 在页面卸载或需要时获取长任务数据
window.addEventListener('unload', () => {
  const longTasks = getLongTasks();
  console.log('检测到的长任务:', longTasks);
  // 可通过sendBeacon发送到服务器
});

关键属性说明

  • duration:长任务持续时间(超过50ms)
  • attribution:包含任务来源信息,如:
    {
      containerType: 'iframe',  // 任务发生在iframe中
      containerSrc: 'https://example.com/ads',  // iframe源
      containerId: 'ad-iframe',  // iframe ID
      containerName: 'sponsored-content'  // iframe名称
    }
    

二、手动检测(兼容性方案)

原理:通过 requestIdleCallback 或定时检查任务执行时间,手动识别长任务。

function detectLongTasks() {
  const longTasks = [];
  let lastTime = performance.now();
  
  // 使用requestIdleCallback(现代浏览器)
  if ('requestIdleCallback' in window) {
    const checkLongTasks = (deadline) => {
      // 计算距离上次检查的时间
      const now = performance.now();
      const taskDuration = now - lastTime;
      
      if (taskDuration > 50) {  // 长任务阈值
        longTasks.push({
          duration: taskDuration,
          startTime: lastTime
        });
      }
      
      lastTime = now;
      requestIdleCallback(checkLongTasks, { timeout: 500 });
    };
    
    requestIdleCallback(checkLongTasks);
  } else {
    // 降级方案:使用setTimeout(兼容性更好)
    setInterval(() => {
      const now = performance.now();
      const taskDuration = now - lastTime;
      
      if (taskDuration > 50) {
        longTasks.push({
          duration: taskDuration,
          estimatedStartTime: now - taskDuration
        });
      }
      
      lastTime = now;
    }, 100);
  }
  
  return () => longTasks;
}

局限性

  • 无法精确定位任务来源(如具体脚本)
  • 依赖定时器,可能漏检短时长任务(如51ms的任务可能被拆分为两次检测)

三、使用第三方库(简化实现)

1. web-vitals(Google官方库)

import { getLongTasks } from 'web-vitals';

getLongTasks(({ entries }) => {
  entries.forEach(entry => {
    console.log('长任务:', entry);
    // 发送到监控系统
    sendToAnalytics('longtask', {
      duration: entry.duration,
      startTime: entry.startTime,
      attribution: entry.attribution
    });
  });
});

2. Performance Monitor(Chrome扩展) 直接在Chrome开发者工具中可视化长任务:

  1. 打开Chrome DevTools
  2. 切换到"Performance"面板
  3. 点击"Record"开始录制
  4. 操作页面后停止录制,查看"Long Tasks"条目

四、优化建议

  1. 识别长任务来源

    • 使用attribution信息定位问题脚本/组件
    • 关注iframe中的第三方内容(广告、评论组件等)
  2. 长任务拆分

    // 将耗时操作拆分为小任务
    function processLargeData(data) {
      const chunkSize = 1000;
      let index = 0;
      
      const processChunk = () => {
        const chunk = data.slice(index, index + chunkSize);
        // 处理数据块...
        
        index += chunkSize;
        if (index < data.length) {
          requestIdleCallback(processChunk);  // 在下一个空闲时段继续处理
        }
      };
      
      requestIdleCallback(processChunk);
    }
    
  3. 使用Web Workers

    // 在主线程中
    const worker = new Worker('heavy-task.js');
    worker.postMessage(data);
    worker.onmessage = (e) => {
      // 处理结果
    };
    
    // heavy-task.js(Web Worker)
    self.onmessage = (e) => {
      // 执行耗时计算
      const result = heavyCalculation(e.data);
      self.postMessage(result);
    };
    

五、面试延伸问题

  1. 为什么选择50ms作为长任务阈值?
    → 基于RAIL模型,浏览器需要每16ms(60FPS)渲染一帧,50ms阈值确保用户不会感知到明显卡顿。

  2. 长任务和主线程阻塞的关系是什么?
    → 长任务是主线程阻塞的主要原因,会导致:

    • 输入延迟(如点击无响应)
    • 动画/滚动卡顿
    • 无法及时处理网络响应
  3. 如何在生产环境中持续监控长任务?
    → 结合以下方法:

    • 使用PerformanceObserver收集长任务数据
    • 通过sendBeacon发送到监控服务器
    • 对数据进行聚合分析(如计算P95值)

六、总结

方法优点缺点
PerformanceObserver精准、直接获取长任务信息兼容性(IE不支持)
手动检测兼容性好精度低、无法定位来源
第三方库简单易用增加包体积

推荐组合使用 PerformanceObserverrequestIdleCallback,既能精准捕获长任务,又能在不支持的浏览器中降级处理。对于生产环境,建议将长任务数据发送到监控系统,定期分析以持续优化页面性能。