Skip to content

参考资料

mdn 性能API

前端监控

Performance集成的性能指标

每个性能指标都用接口实例PerformanceEntry表示,你可通过Performance.getEntries() 或 (更推荐) 通过 PerformanceObserver,因为观察者通知是异步传递的,因此浏览器可以在空闲时间调度它们,以最大限度地减少它们对性能的影响。

performanceObserver API兼容性较好,在2016-2017开始普遍支持,而PerformanceNavigationTiming兼容性则不太理想,Safari浏览器在2021年才开始支持,所有需要做兼容处理使用performance.timing

比较通用的是以下这些指标

  • "largest-contentful-paint"记录页面加载过程中最大的绘制。Safari不支持
  • "longtask"记录耗时 50 毫秒或以上的任务。
  • "navigation"记录与导航到页面和页面初始加载相关的指标,返回PerformanceNavigationTiming
  • "paint"记录页面加载过程中渲染的关键时刻,包含首次绘制(FP)和首次内容绘制(FCP),兼容性方面也比较差,FCP Safari浏览器在2021年才开始支持,FP则Safari不支持
  • "resource"记录浏览器获取资源所花的时间。

PerformanceNavigationTiming

除了上面性能指标外,我们还可以通过performance的性能时间指标自己计算相关性能指标,比如DOM Ready时间、TTI首次可交互时间、LOAD时间、服务器的响应时间

官方图片

  1. domInteractive HTML 解析完成,DOM 树构建完成
  2. domContentLoadedEventStart触发 DOMContentLoaded 事件
  3. domContentLoadedEventEnd DOMContentLoaded 事件处理完成
  4. domComplete DOM 所有资源加载完成
  5. loadEventStart所有资源加载完成,触发 load 事件
  6. loadEventEnd load 事件处理完成,页面完全加载

performance.now() 和 Date.now()区别

performance.now()是页面加载(navigationStart)后经过的毫秒数,是微秒级高精度相对值,而 Date.now()是1970年到现在的时间戳,是绝对值

js
performance.now()+performance.timeOrigin===Date.now()

指标详解

  • LargestContentfulPaint

    从页面第一次开始加载时记录。测量视口中可见的最大图像或文本块的呈现时间,一般指的是首屏时间

  • PerformancePaintTiming

    FP和FCP的区别FCP是首次绘制有效内容的时间点;所以FP会等于或者先于FCP

  • PerformanceResourceTiming

    网络加载的指标,像script、css、link等资源的加载时间

值得注意的是流式解析与渲染

  • 浏览器接收 HTML 的过程中,不必等待整个文档加载完成,而是随着数据到达,逐步解析和渲染内容。

  • 在解析和渲染的过程中,遇到css会去下载,并且会阻塞渲染和js的执行、遇到普通的js会阻塞DOM解析

  • 在html开始正式解析之前,还会有一个预加载扫描器。这个步骤不是真正去解析html而是扫描分析文件中是否有外部引用文件,如果有的话就会开始下载。

获取Time兼容写法

typescript
const getTiming = () => {
  let timing: Timing = {
    fetchStart: Date.now(),
  };
  if (!performance) {
    return timing;
  }
  if ("timing" in performance) {
    timing = performance.timing;
  } else if ("getEntriesByType" in performance && "timeOrigin" in performance) {
    const navigationEntries = window.performance.getEntriesByType("navigation");
    // 检查是否有 navigation 条目
    if (navigationEntries.length > 0) {
      timing = (navigationEntries[0] as PerformanceNavigationTiming); // 类型断言

      let validKeys = [
        "fetchStart",
        "responseEnd",
        "domInteractive",
        "domContentLoadedEventEnd",
        "loadEventStart",
      ] as const;
      let res: Partial<Timing> = {};
      // 将相对时间转为绝对时间
      validKeys.forEach((key) => {
        // @ts-ignore
        res[key] = performance.timeOrigin + timing[key];
      });
      timing={...timing,...res}
    }
  }
  return timing;
};

TTI指标

 timing.domInteractive - timing.fetchStart

FMP计算

通过MutationObserver 监听body节点变化,每变化一次,计算一次body分数,取分数突变最大的那一次为首屏时间。分数计算逻辑是在视口内1+0.5*depth,不在视口内为0

遍历是深度优先遍历、如何子可见,父级不用计算一定可见、最后一个兄弟元素可见、前面兄弟元素也一定可见