13

改善页面性能 - 如何监控资源加载

 3 years ago
source link: https://zhuanlan.zhihu.com/p/59285705
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

改善页面性能 - 如何监控资源加载

为了提供给前端者监控页面性能的能力,W3C 定义了一系列相关 API,在这里统称为 Performance API。目前使用较多的应该是 PerformanceTiming,但是除了该 API,新的 W3C 草案及 WICG 提案定义了一系列 PerformanceEntry API,不仅取代了原 PerformanceTiming 的能力,并增加了更多维度的信息,本文主要针对这些 API 进行介绍。

前两篇文章中我们简要介绍了 Performance 的能力,详细介绍了 Long Task API 以及 Event Timing API,并用实例展示了如何使用。本篇我们会介绍交互相关的 First Input Timing API,资源加载相关的 Resource Timing API 以及 Navigation Timing API。希望 Performance 系列能更好的帮助开发者去优化页面性能。

First Input Timing

由于页面加载完成后的第一次事件处理对用户体验的影响非常大,而且通常它的响应又比较慢,所以提案针对第一次输入事件做了专门的定义。这里的 first input 指下面几种事件,不包括滚动:

  • Key down
  • Mouse down
  • Pointer down which is followed by a pointer up
  • Click

First Input Entry 生成流程:

在PerformanceEventTiming 的生成流程最后,如果判断它为第一次输入事件,则会从原来的 Entry 中拷贝一个新的 Entry 用来表示 first input,并加入队列。first input Entry 有 2 个注意点:

  1. 它虽然也是一个 PerformanceEventTiming 对象,但是 entryType 变成 “firstInput”
  2. 对于 pointer 事件,第一个产生的事件是 pointerdown,但是这里并不会记录 pointerdown,而是记录随后而来的 pointerup
  • 点击按钮后表格中的数据重新排序。我们可以使用该 API 计算出从开始点击到显示排序后内容的时间消耗
  • 用户拖动控制音量的滑块。我们可以使用该 API 计算拖动滑块的响应时间

从 Chrome Platform Status 网站上查到该 API 目前还处于开发状态的修改问题阶段。

Resource Timing

传统的开发者可以使用 js 去监控 js 脚本加载的资源时间,但是这种做法有 2 个问题:

  • 在标签里定义的资源很难被监控
  • 获取的时间信息维度比较单一

为了解决上述问题,W3C 定义了 Resource Timing API,方便前端开发者获取更加丰富的页面资源加载信息。

PerformanceResourceTiming 接口定义:

[Exposed=(Window,Worker)]interfacePerformanceResourceTiming: PerformanceEntry {
    readonly attribute DOMString           initiatorType;
    readonly attribute DOMString           nextHopProtocol;
    readonly attribute DOMHighResTimeStamp workerStart;
    readonly attribute DOMHighResTimeStamp redirectStart;
    readonly attribute DOMHighResTimeStamp redirectEnd;
    readonly attribute DOMHighResTimeStamp fetchStart;
    readonly attribute DOMHighResTimeStamp domainLookupStart;
    readonly attribute DOMHighResTimeStamp domainLookupEnd;
    readonly attribute DOMHighResTimeStamp connectStart;
    readonly attribute DOMHighResTimeStamp connectEnd;
    readonly attribute DOMHighResTimeStamp secureConnectionStart;
    readonly attribute DOMHighResTimeStamp requestStart;
    readonly attribute DOMHighResTimeStamp responseStart;
    readonly attribute DOMHighResTimeStamp responseEnd;
    readonly attribute unsigned long long  transferSize;
    readonly attribute unsigned long long  encodedBodySize;
    readonly attribute unsigned long long  decodedBodySize;[Default] object toJSON();
};

这里大部分字段都比较好理解,表示一个资源加载过程各个细分时间信息,见上图。有几个字段特别说明一下:

initiatorType 记录了资源的加载来源:

  • 如果资源加载来源于页面标签,则 initiatorType 等于 element.localName
  • 如果资源加载来源于 css,例如 @import url() or background: url() 这样的写法,则 initiatorType等于字符串 "css"
  • 如果资源加载来源于 XMLHttpRequest,则 initiatorType 等于字符串 "xmlhttprequest"
  • 如果是 Navigation 请求,则 initiatorType 等于字符串 "Navigation"
  • 如果是来源于 fetch 方法,则 initiatorType 等于字符串 "fetch"
  • 如果是来源于 sendBeacon 方法,则 initiatorType 等于字符串 "beacon"
  • 其它情况则 initiatorType 等于字符串 "other"

nextHopProtocol 记录了此次资源加载使用的网络协议:

  • 如果该资源从缓存或者本地加载,则该字段为空字符串
  • 如果从网络加载,则记录一个 ALPN Protocol ID

workerStart:当存在 ServiceWorker 时,workerStart 才有值,它记录了 ServiceWorker 启动的时间,或者调用 fetch 的时间(如果 ServiceWorker 已经启动);若不存在 ServiceWorker 该值为 0。

标准定义中,除了新增 PerformanceResourceTiming,还定义了 Performance 的扩展:

partial interfacePerformance{
    void clearResourceTimings();
    void setResourceTimingBufferSize(unsigned long maxSize);
    attribute EventHandler onresourcetimingbufferfull;
};

介绍 Performance 新增的接口之前,先了解几个概念:

  • performance entry buffer:PerformanceEntry 对象缓冲区,保存了各种 PerformanceEntry 对象,其中也包括了 PerformanceResourceTiming,初始为空
  • resource timing buffer size limit:PerformanceResourceTiming 在 performance entry buffer 的最大 size 限制,W3C 建议是大于 150,但是用户可以通过 setResourceTimingBufferSize() 自由设置
  • resource timing buffer current size:表示当前 performance entry buffer 中 PerformanceResourceTiming 对象的个数,初始值为 0
  • resource timing buffer full flag:表示当前 performance entry buffer 中 PerformanceResourceTiming 的个数是否达到 limit,初始为 false

Performance 新增接口主要提供了操作 performance entry buffer 的方法:

  • clearResourceTiming() :清除 performance entry buffer 里的所有的 PerformanceResourceTiming 对象,并把相关标志置位
  • setResourceTimingBufferSize():用来设置 PerformanceResourceTiming 在 performance entry buffer 的最大 size 限制

使用方法示例:

<!doctype html><html><head></head><body onload="loadResources()"><script>
       function loadResources() {
          var image1 = new Image();
          image1.onload = resourceTiming;
          image1.src = 'https://www.w3.org/Icons/w3c_main.png';
       }

       function resourceTiming() {
           var resourceList = window.performance.getEntriesByType("resource");
           for (i = 0; i < resourceList.length; i++) {
              if (resourceList[i].initiatorType == "img") {
                 alert("End to end resource fetch: "+ resourceList[i].responseEnd - resourceList[i].startTime);
              }
           }
       }
    </script><img id="image0"src="https://www.w3.org/Icons/w3c_home.png"></body></html>

除了上述用法,应该也可以通过添加 PerformanceObserver 的方式去监听变化,可以参考 Performance API 的使用示例。

Navigation Timing

Navigation TIming API 跟 Resource Timing API 其实类似,但是它记录的不是资源的加载信息,而是页面加载过程的时间信息。

PerformanceNavigationTiming 继承于 PerformanceResourceTiming:

[Exposed=Window]interfacePerformanceNavigationTiming: PerformanceResourceTiming {
    readonly attribute DOMHighResTimeStamp unloadEventStart;
    readonly attribute DOMHighResTimeStamp unloadEventEnd;
    readonly attribute DOMHighResTimeStamp domInteractive;
    readonly attribute DOMHighResTimeStamp domContentLoadedEventStart;
    readonly attribute DOMHighResTimeStamp domContentLoadedEventEnd;
    readonly attribute DOMHighResTimeStamp domComplete;
    readonly attribute DOMHighResTimeStamp loadEventStart;
    readonly attribute DOMHighResTimeStamp loadEventEnd;
    readonly attribute NavigationType      type;
    readonly attribute unsigned short      redirectCount;[Default] object toJSON();
};

PerformanceNavigationTiming 主要是在 PerformanceResourceTiming 基础上扩展了几个新字段,具体定义通过上图可以很容易理解。有几个需要注意:

  • PerformanceNavigationTiming 的 entryType 为 "navigation"
  • initiatorType 为常量 "navigation"
  • unloadEventStart:记录 unload() 事件的起始点,但是只有在 2 个页面同源(origin)的时候才会记录,非同源该值为 0
  • unloadEventEnd:记录 unload() 事件的结束点,也是只有在同源的时候才会记录
  • type:有以下几种:
enum NavigationType {"navigate","reload","back_forward","prerender"};

最新最好的内核技术文章,请搜索并关注公众号"U4内核技术"或"u4core"。




About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK