面试题-设计⼀套全站请求耗时统计工具

160 阅读2分钟

在前端实现性能 sdk,要求采集首屏性能、完全加载、tti、白屏、卡顿、接口请求耗时等技术指标。

以下是一个前端性能 SDK 的实现方案,包含主要指标的采集逻辑和代码示例:

1. 首屏性能 (First Contentful Paint)

// 监听首屏绘制事件
const observer = new PerformanceObserver((list) => {
  const entry = list.getEntries()[0];
  console.log('First Contentful Paint:', entry.startTime);
  // 上报数据
});
observer.observe({ type: 'paint', buffered: true });

2. 完全加载时间 (Load Event End)

window.addEventListener('load', () => {
  const loadTime = performance.now();
  console.log('Page Load Time:', loadTime);
});

3. TTI (Time to Interactive)

let isInteractive = false;
let ttiTime = 0;

// 检测用户交互事件
['click', 'touchstart', 'keydown'].forEach(event => {
  window.addEventListener(event, () => {
    if (!isInteractive) {
      ttiTime = performance.now();
      isInteractive = true;
      console.log('TTI:', ttiTime);
    }
  });
});

// 检测长任务(>50ms)
const longTaskObserver = new PerformanceObserver((list) => {
  const entries = list.getEntries();
  entries.forEach(entry => {
    if (entry.duration > 50) {
      // 重置TTI检测
      isInteractive = false;
      ttiTime = 0;
    }
  });
});
longTaskObserver.observe({ entryTypes: ['longtask'] });

4. 白屏时间 (White Screen Duration)

let whiteScreenStart = 0;

// 记录开始时间
const beforeLoad = performance.timing.navigationStart;
whiteScreenStart = beforeLoad;

// 检测首次渲染
const firstRenderObserver = new PerformanceObserver((list) => {
  const entry = list.getEntries()[0];
  const duration = entry.startTime - whiteScreenStart;
  console.log('White Screen Duration:', duration);
});
firstRenderObserver.observe({ type: 'render', buffered: true });

5. 卡顿检测 (FPS Monitoring)

let lastFrameTime = performance.now();
let frameCount = 0;
let isFreezing = false;

function checkFPS() {
  const now = performance.now();
  const delta = now - lastFrameTime;
  lastFrameTime = now;
  
  frameCount++;
  if (delta > 16) { // 60fps对应的16ms间隔
    if (!isFreezing) {
      isFreezing = true;
      console.log('页面卡顿开始');
    }
  } else {
    isFreezing = false;
  }
  
  // 每秒钟计算一次平均帧率
  if (now - lastFrameTime >= 1000) {
    const fps = frameCount;
    console.log(`Current FPS: ${fps}`);
    frameCount = 0;
  }
  
  requestAnimationFrame(checkFPS);
}
requestAnimationFrame(checkFPS);

6. 接口请求耗时 (API Monitoring)

// 重写XMLHttpRequest
const nativeXHR = XMLHttpRequest;
XMLHttpRequest = function() {
  const xhr = new nativeXHR();
  const send = xhr.send;
  
  xhr.send = function(...args) {
    const startTime = performance.now();
    xhr.addEventListener('load', () => {
      const duration = performance.now() - startTime;
      console.log(`API ${xhr.url}耗时: ${duration}ms`);
    });
    send.apply(xhr, args);
  };
  
  return xhr;
};

// 监听fetch请求
const nativeFetch = window.fetch;
window.fetch = async (...args) => {
  const startTime = performance.now();
  const response = await nativeFetch(...args);
  const duration = performance.now() - startTime;
  console.log(`Fetch ${args[0]}耗时: ${duration}ms`);
  return response;
};

7. 数据上报

function sendData(data) {
  const body = JSON.stringify(data);
  const xhr = new XMLHttpRequest();
  xhr.open('POST', '/api/performance', true);
  xhr.setRequestHeader('Content-Type', 'application/json');
  xhr.send(body);
}

// 示例上报数据格式
const reportData = {
  timestamp: Date.now(),
  metrics: {
    fcp: 1200,
    loadTime: 2500,
    tti: 3000,
    whiteScreen: 500,
    fps: 58,
    apiRequests: [
      { url: '/api/data', duration: 350 },
      { url: '/api/config', duration: 120 }
    ]
  }
};
sendData(reportData);

实现要点:

  1. 性能优化

    • 使用requestIdleCallback处理非紧急任务
    • 数据上报采用navigator.sendBeacon保证可靠性
    • 对监控逻辑进行采样处理(如5%的用户采样)
  2. 兼容性处理

    • 对不支持的API使用polyfill
    • 优雅降级处理无法获取的指标
  3. 用户体验

    • 避免影响主线程性能
    • 提供配置开关
    • 最小化内存占用
  4. 扩展能力

    • 支持自定义指标采集
    • 可配置的上报规则
    • 插件化架构设计

建议结合真实用户监控(RUM)和实验室数据(Lighthouse)进行综合性能分析,持续优化页面加载和交互体验。