Performance Observer - 高效访问性能数据

Marc Cohen

借助渐进式 Web 应用,开发者可以构建一类全新的应用,以提供可靠、高性能的用户体验。 但为了确保 Web 应用实现所需的性能目标,开发者需要访问高分辨率的性能衡量数据。W3C 性能时间轴规范定义了一个此类接口,供浏览器以编程方式访问低层级时间数据。这就打开了一些有趣的用例的大门:

  • 离线和自定义性能分析
  • 第三方效果分析和可视化工具
  • 将性能评估集成到 IDE 和其他开发者工具中

在大多数主流浏览器中,已经可以在导航时间资源计时用户计时方面访问此类计时数据。最新增加的是 Performance Observer 接口,它本质上是一个流式传输接口,用于根据浏览器收集的低级时间信息异步收集低级时间信息。与以前的时间轴访问方法相比,这个新接口具有许多关键优势:

  • 如今,应用必须定期轮询和区分存储的测量值,成本很高。该接口会为它们提供回调。(换句话说,不需要轮询)。因此,使用此 API 的应用响应速度更快、效率更高。
  • 它不受缓冲区限制的约束(大多数缓冲区默认设置为 150 个项),并且可以避免可能需要修改缓冲区的不同使用方之间的竞态条件。
  • 性能观察者通知以异步方式传送,浏览器可以在空闲时间调度这些通知,以避免与关键渲染工作发生争用。

从 Chrome 52 开始,性能观察器界面默认处于启用状态。我们来看看如何使用它。

<html>
<head>
    <script>
    var observer = new PerformanceObserver(list => {
        list.getEntries().forEach(entry => {
        // Display each reported measurement on console
        if (console) {
            console.log("Name: "       + entry.name      +
                        ", Type: "     + entry.entryType +
                        ", Start: "    + entry.startTime +
                        ", Duration: " + entry.duration  + "\n");
        }
        })
    });
    observer.observe({entryTypes: ['resource', 'mark', 'measure']});
    performance.mark('registered-observer');

    function clicked(elem) {
        performance.measure('button clicked');
    }
    </script>
</head>
<body>
    <button onclick="clicked(this)">Measure</button>
</body>
</html>

下面这个简单的网页以定义一些 JavaScript 代码的脚本代码开头:

  • 我们会实例化一个新的 PerformanceObserver 对象,并将事件处理脚本函数传递给对象构造函数。该构造函数会初始化该对象,以便每次准备好处理一组新的测量数据(测量数据作为对象列表进行传递)时,系统都会调用我们的处理程序。此处的处理程序定义为匿名函数,仅用于在控制台中显示格式化的测量数据。在实际场景中,这些数据可能会存储在云端以进行后续分析,或传输到交互式可视化工具中。
  • 我们通过 observe() 方法注册感兴趣的计时事件类型,并调用 mark() 方法以标记注册时间点,我们将这一时间视为计时间隔的开始时间。
  • 我们为页面正文中定义的按钮定义了一个点击处理程序。此点击处理程序会调用 measure() 方法以捕获有关何时点击按钮的时间数据。

在页面的正文中,我们定义一个按钮,为 onclick 事件分配点击处理程序,然后就可以开始了。

现在,如果我们加载页面并打开 Chrome 开发者工具面板以查看 JavaScript 控制台,则每次点击该按钮时,系统都会进行性能测量。由于我们已针对此类测量结果进行注册,因此它们会被异步转发到我们的事件处理脚本,而无需轮询时间轴,时间轴会在测量结果发生时将其显示在控制台中:

Performance Observer。

start 值表示 mark 类型的事件(此应用只有一个事件)的开始时间戳。measure 类型的事件没有固有的开始时间;它们表示相对于上一个 mark 事件进行的时间测量。因此,此处看到的时长值表示调用 mark()(用作公共间隔的起点)与多次后续调用 measure() 之间的间隔时间。

如您所见,此 API 非常简单,它无需轮询即可收集经过过滤的高分辨率实时性能数据,这可以为 Web 应用开启更高效的性能工具的大门。