Performance系列 - Performance API

性能优化

2019-02-28

# 背景

为了提供给前端者监控页面性能的能力,W3C 定义了一系列相关 API,在这里统称为 Performance API。目前使用较多的应该是 PerformanceTiming,但是除了该 API,新的 W3C 草案及 WICG 提案定义了一系列 PerformanceEntry API,不仅取代了原 PerformanceTiming 的能力,并增加了更多维度的信息,本文主要针对这些 API 进行介绍。 为了更好地理解目前这些 API 的成熟度,先简单介绍一下 W3C 标准发布流程: 在 W3C 发布某个新标准的过程中,规范是通过下面的严格程序由一个简单的理念逐步确立为推荐标准的:

  • W3C 收到一份提交
  • 由 W3C 发布一份记录
  • 由 W3C 创建一个工作组
  • 由 W3C 发布一份工作草案
  • 由 W3C 发布一份候选的推荐
  • 由 W3C 发布一份被提议的推荐
  • 由 W3C 发布推荐

文档演进过程: W3C 提交 (W3C Submissions) --> W3C 记录 (W3C Notes) --> W3C 工作组 (W3C Working Groups) --> W3C 工作草案 (W3C Working Drafts) --> W3C 候选推荐 (W3C Candidate Recommendations) --> W3C 提议推荐 (W3C Proposed Recommendations) --> W3C 推荐 (W3C Recommendations)

# PerformanceTiming

早期的 Performance 相关信息主要是由 Navigation Timing Recommendations 定义的 PerformanceTiming 提供,包含了加载过程的细分信息:

interfacePerformanceTiming{
  readonly attribute unsigned long long navigationStart;
  readonly attribute unsigned long long unloadEventStart;
  readonly attribute unsigned long long unloadEventEnd;
  readonly attribute unsigned long long redirectStart;
  readonly attribute unsigned long long redirectEnd;
  readonly attribute unsigned long long fetchStart;
  readonly attribute unsigned long long domainLookupStart;
  readonly attribute unsigned long long domainLookupEnd;
  readonly attribute unsigned long long connectStart;
  readonly attribute unsigned long long connectEnd;
  readonly attribute unsigned long long secureConnectionStart;
  readonly attribute unsigned long long requestStart;
  readonly attribute unsigned long long responseStart;
  readonly attribute unsigned long long responseEnd;
  readonly attribute unsigned long long domLoading;
  readonly attribute unsigned long long domInteractive;
  readonly attribute unsigned long long domContentLoadedEventStart;
  readonly attribute unsigned long long domContentLoadedEventEnd;
  readonly attribute unsigned long long domComplete;
  readonly attribute unsigned long long loadEventStart;
  readonly attribute unsigned long long loadEventEnd;};

每个属性的含义用下面的图可以直观地描述:

1.png

后面 Chrome 实现了 PerformanceEntry 相关的新 API,提供了更加丰富和详细的信息。在 Navigation Timing Level 2 草案中,已经废弃了 PerformanceTiming 接口,并且提供了新的接口 PerformanceNavigationTiming 代替其功能,详见下面的介绍。

# PerformanceEntry

PerformanceEntry 是 W3C Candidate Recommendation 定义的接口,具体的几种 Entry 类型可以参考 EntryType,但是这里并没有列出全部。 3个核心 API 定义如下: PerformanceEntry:

interfacePerformanceEntry{
    readonly attribute DOMString           name;
    readonly attribute DOMString           entryType;
    readonly attribute DOMHighResTimeStamp startTime;
    readonly attribute DOMHighResTimeStamp duration;
    serializer ={attribute};};

Performance:

partial interfacePerformance{
    PerformanceEntryList getEntries();
    PerformanceEntryList getEntriesByType(DOMString type);
    PerformanceEntryList getEntriesByName(DOMString name,
                                          optional DOMString type);
};

typedef sequence<PerformanceEntry> PerformanceEntryList;

PerformanceObserver:

dictionary PerformanceObserverInit {
    required sequence<DOMString> entryTypes;
};
[Exposed=(Window,Worker)] interfacePerformanceObserverEntryList {
    PerformanceEntryList getEntries();
    PerformanceEntryList getEntriesByType(DOMString type);
    PerformanceEntryList getEntriesByName(DOMString name,
                                          optional DOMString type);
};

callback PerformanceObserverCallback = void(PerformanceObserverEntryList entries,                                                 PerformanceObserver observer);[Constructor(PerformanceObserverCallback callback),
 Exposed=(Window,Worker)] interfacePerformanceObserver {
   voidobserve(PerformanceObserverInit options);voiddisconnect();
 };

使用示例:

<!doctype html>
<html>
<head>
</head>
<body onload="init()">
  <img id="image0"src="https://www.w3.org/Icons/w3c_main.png"/>
  <script>
    function init() {
      performance.mark("startWork"); // see [USER-TIMING]
      doWork(); // Some developer code
      performance.mark("endWork");
      measurePerf();
    }
    function measurePerf() {
      var perfEntries = performance.getEntries();
      for (var i = 0; i < perfEntries.length; i++) {
        if (window.console) {
          console.log("Name: "        + perfEntries[i].name      +
                      " Entry Type: " + perfEntries[i].entryType +
                      " Start Time: " + perfEntries[i].startTime +
                      " Duration: "   + perfEntries[i].duration  + "\n");
        }
      }
    }
  </script>
</body>
</html>
<!doctype html>
<html>
<head>
</head>
<body>
  <img id="image0"src="https://www.w3.org/Icons/w3c_main.png"/>
  <script>
    var doneObservingEvents = false;
    var observer = new PerformanceObserver(function(list) {
      var perfEntries = list.getEntries();
      for (var i = 0; i < perfEntries.length; i++) {
        if (window.console) {
          console.log("Name: "        + perfEntries[i].name      +
                      " Entry Type: " + perfEntries[i].entryType +
                      " Start Time: " + perfEntries[i].startTime +
                      " Duration: "   + perfEntries[i].duration  + "\n");
        }
      }
      // maybe disconnect after processing the events.
      if (doneObservingEvents) {
        observer.disconnect();
      }
    });
    // subscribe to Resource-Timing and User-Timing events
    observer.observe({entryTypes: ['resource', 'mark', 'measure']});
  </script>
</body>
</html>

查看 Chromium 源码,m68 版本现在主要有下面几种 PerformanceEntry(除了 PerformanceEventTiming 是较新版本增加,其它的 m57 版本均已存在,但是 m57 版本看起来代码还不成熟): 2.png

  • TaskAttributionTiming:配合着 PerformanceLongTaskTiming 来使用,主要是记录跟 long task 关联的节点信息;
  • PerformanceLongTaskTiming:用来获取处理时间较长的任务信息,后面我们会陆续发文介绍,敬请关注;
  • PerformanceEventTiming:用来获取处理时间较长的 input 事件信息,后面我们会陆续发文介绍,敬请关注;
  • PerformanceResourceTiming:用来监控页面所以资源的加载时间,后面我们会陆续发文介绍,敬请关注;
  • PerformanceNavigationTiming:用来记录页面加载过程的时间信息,它代替了 PerformanceTiming API 的功能,后面我们会陆续发文介绍,敬请关注;
  • PerformancePaintTiming:给前端开发者提供 first paint 和 first contentful paint 信息,后面我们会陆续发文介绍,敬请关注;
  • PerformanceMark & PerformanceMeasure:这2个 Entry 主要是结合 Performance 接口,提供用户自定义跟踪 js 脚本性能的方法,后面我们会陆续发文介绍,敬请关注;

对于 WICG draft 提到的 PerformanceFrameTiming,Chrome 目前还没有相关实现代码。