11

改善页面性能 - 如何监控卡顿和响应延迟

 3 years ago
source link: https://zhuanlan.zhihu.com/p/58605646
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 的能力,并用实例展示了如何使用该 API。从本篇开始,我们会更详细的介绍 Performance 提供的一些细节能力,提供示例。希望 Performance 系列能更好的帮助开发者去优化页面性能。

Long Task API

介绍

v2-722d45aa73d598849ceed30e7c5c9126_720w.jpgRAIL 模型 - Response,Animation,Idle,Load

当页面有 task(这里的 task 指的是内核消息循环里的一个任务)执行时间超过 50ms,内核则会通过 Long Task API 将该任务的相关信息回调给前端。把 long task 时间定义为 50ms 的主要理论依据是 Chrome 提出的 RAIL 模型,RAIL 认为事件响应应该在 100ms 以内,滚动和动画处理应该在 16ms 以内,才能保证好的用户体验,而如果一个 task 执行超过 50ms,则很有可能让体验达不到 RAIL 的标准,故我们需要重点关注执行时间超过 50ms 的任务。

PerformanceLongTaskTiming 定义:

interface PerformanceLongTaskTiming : PerformanceEntry {
  [SameObject, SaveSameObject] readonly attribute FrozenArray<TaskAttributionTiming> attribution;
};

TaskAttributionTiming 定义:

interface TaskAttributionTiming : PerformanceEntry {
  readonly attribute DOMString containerType;
  readonly attribute DOMString containerSrc;
  readonly attribute DOMString containerId;
  readonly attribute DOMString containerName;
};

TaskAttributionTiming 配合着 PerformanceLongTaskTiming 使用,记某个 long task 相关信息,具体字段定义如下:

  • containerType: long task 来源的 frame 类型,这里包括 "iframe" (most common), "embed", "object"
  • containerName: 包含 long task 的 element 标签名
  • containerId: 包含 long task 的 element id
  • containerSrc: 包含 long task 的 element src 字段的值

使用示例

var observer = new PerformanceObserver(function(list) {
  var perfEntries = list.getEntries();
  for (var i = 0; i < perfEntries.length; i++) {
     // Process long task notifications:
     // report back for analytics and monitoring
     // ...
  }
});


// register observer for long task notifications
observer.observe({entryTypes: ["longtask"]});

// Long script execution after this will result in queueing 
// and receiving “longtask” entries in the observer.

当前进展

Chromium m57 代码已经有 PerformanceLongTaskTiming 的实现,代码在 2016-08-25 m57 分支上面首次提交,接口已经符合规范,但是具体实现不确定是否完善。从 PerformanceLongTaskTiming 对象提供的属性来看,它提供的信息粒度还是比较粗,如果某个 JS 方法执行时间过长,并没有信息可以直接指向该方法。

Event Timing

介绍

点击事件是移动场景中的主要事件之一

Event Timing 是 WICG 的一个提案,主要是提供 API 来跟踪处理时间超过 50ms 的输入事件,这些事件类型包括:

  • MouseEvents
  • PointerEvents
  • TouchEvents
  • KeyboardEvents
  • WheelEvents
  • InputEvents
  • CompositionEvents

它提供了以下 3 个接口:

interfacePerformanceEventTiming: PerformanceEntry {// The type of event dispatched. E.g. "touchmove".// Doesn't require an event listener of this type to be registered.
    readonly attribute DOMString name;// "event".
    readonly attribute DOMString entryType;// The event timestamp.
    readonly attribute DOMHighResTimeStamp startTime;// The time the first event handler started to execute.// |startTime| if no event handlers executed.
    readonly attribute DOMHighResTimeStamp processingStart;// The time the last event handler finished executing.// |startTime| if no event handlers executed.
    readonly attribute DOMHighResTimeStamp processingEnd;// The duration between |startTime| and the next time we "update the rendering // or user interface of that Document and its browsing context to reflect the // current state" in step 7.12 in the HTML event loop processing model.
    readonly attribute DOMHighResTimeStamp duration;// Whether or not the event was cancelable.
    readonly attribute boolean cancelable;};// Contains the number of events which have been dispatched, per event type.interfaceEventCounts{
  readonly attribute unsigned long click;...
  readonly attribute unsigned long touchmove;...};

partial interfacePerformance{// Contains the number of events which have been dispatched, per event type. Populated asynchronously. 
    readonly attribute EventCounts eventCounts;};

PerformanceEventTiming 记录了某个事件的相关信息,具体字段如下:

  • name:保存的是 event.type 字段,表示一个事件的类型,例如 touchstart、touchmove
  • entryType:表示 entry 的类型,这里是一个常量 "event"
  • startTime:保存 event.timeStamp 字段,记录了事件生成时的时间戳
  • processingStart:记录内核开始处理事件的时间点
  • processingEnd:记录内核处理完事件的时间点
  • duration:在处理完事件时,通过 Math.ceil((performance.now() - newEntry.startTime)/8) * 8 计算,这里的处理主要是为了减少时间精度,增加安全性
  • cancelable:保存 event.cancelable 字段,表示事件是否可以被取消
  • Performance 接口只有一个 eventCounts 属性,它记录了所有已经分发过的 Event,处理时间是否大于 50ms

使用示例

const performanceObserver = new PerformanceObserver((entries) => {
  for (const entry of entries.getEntries()) {
      console.log(entry);
  }
});

performanceObserver.observe({entryTypes:['event']});

跟其他的 PerformanceEntry 一样,都是通过 PerformanceObserver 来监听回调。

当前进展

查看源码,Chromium m57 还未实现 PerformanceEventTiming 相关逻辑,但是 m68 已经有 PerformanceEventTiming 对象,相关代码在 2018-05-08 首次提交,但是跟这里描述的并不一致,只有 processingStart 和 cancelable 2 个属性,应该还未完善。

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

v2-ece0953a9b3b7955d5a8557962af5bfc_720w.jpg


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK