Performance Observer - 高效访问性能数据

Marc Cohen

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

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

大多数主要浏览器都已支持访问此类时间数据,以便获取导航时间资源时间用户时间。最新添加的是性能观察器接口,它本质上是一种流式接口,用于异步收集由浏览器收集的低级时间信息。与之前访问时间轴的方法相比,此新界面具有多项关键优势:

  • 目前,应用必须定期轮询存储的测量结果并进行差异化处理,这会产生高昂的开销。此接口会为它们提供回调。(也就是说,无需轮询)。因此,使用此 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 DevTools 面板以监控 JavaScript 控制台,那么每次点击该按钮都会进行性能测量。由于我们已注册以观察此类测量,因此系统会异步将这些测量结果转发给事件处理脚本,而无需轮询时间轴,后者会在测量结果发生时在控制台中显示这些测量结果:

性能观察器。

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

如您所见,此 API 非常简单,能够在不轮询的情况下收集经过过滤的高分辨率实时性能数据,这应该为 Web 应用提供了更高效的性能工具。