什么是 Performance Timeline?
Performance Timeline
是W3C性能小组提出的一个规范,定义了让开发者在应用整个生命周期内收集各种性能指标的接口。
最新的Performance Timeline Level 2
标准,取代了第一版Performance Timeline
标准,它包括了以下三点:
- 扩展了
Performance
接口的基本定义 - 在Web Workers中暴露了
PerformanceEntry
- 增加了
PerformanceObserver
的支持
Performance Timeline 在 Node.js
值得注意的是,在Node.js里同样遵循W3C的Performance Timeline
规范,实现了 Performance Hooks 接口。Performance Hooks 的API和现代浏览器中实现的 Performance API 是一致的。
在浏览器中,我们可以使用 window 对象取得window.performance
和 window.PerformanceObserver
,而在 Node.js 程序中需要 perf_hooks 取得性能对象,如下:
const { PerformanceObserver, performance } = require('perf_hooks');
复制代码
浏览器兼容性
- performance在浏览器中的兼容性
- PerformanceObserver在浏览器中的兼容性
W3C性能小组鼓励开发人员在浏览器兼容性允许的情况下,尽可能使用 PerformanceObserver。另外,新的性能API和指标可能只能通过PerformanceObserver 接口获得。
performance 和 PerformanceObserver 的使用
window.performance
使用window.performance
,我们可以这样去度量某个函数的性能。
function init() {
performance.mark("startWork");
doWork();
performance.mark("endWork");
performance.measure("work", "startWork", "endWork")
measurePerf();
}
function measurePerf() {
performance
.getEntries()
.map(entry => JSON.stringify(entry, null, 2))
.forEach(json => console.log(json));
}
复制代码
在上述代码中,我们使用了performance.mark
和performance.measure
来记录性能记录的数据。
performance.measure
方法会根据 startMark 和 endMark 对应的,由 performance.mark
产生的两条记录,来产生一条 entryType 为 'measure' 的新记录,并计算运行时长。
然后代码里使用了performance.getEntries
来获取浏览器缓冲区中所有的性能数据记录。
当然,我们也可以使用
performance.getEntriesByName
获取指定 entryType 的性能记录。
window.PerformanceObserver
正如上文所述,我们要想获得某项性能记录,需要知道指定的性能事件已经发生(或者使用定时轮训的方式),主动调用performance.getEntries
或者performance.getEntriesByName
来获得。
为了解决这个问题,在Performance Timeline Level 2
中,除了扩展了Performance
的基本定义以外,还增加了PerformanceObserver
接口。
顾名思义,PerformanceObserver
在浏览器内部对Performance
实现了观察者模式,也是现代浏览器支持的几个 Observer 之一。
它解决了以下3点问题:
- 避免不知道性能事件啥时候会发生,需要重复轮训timeline获取记录。
- 避免产生重复的逻辑去获取不同的性能数据指标
- 避免其他资源需要操作浏览器性能缓冲区时产生竞态关系。
在兼容Performance Timeline Level 2
的浏览器或者 Node.js 中,可以这样写:
const userTimingObserver = new PerformanceObserver(list => {
list
.getEntries()
.map(({ name, entryType, startTime, duration }) => {
const obj = {
"Duration": duration,
"Entry Type": entryType,
"Name": name,
"Start Time": startTime,
};
return JSON.stringify(obj, null, 2);
})
.forEach(console.log);
userTimingObserver.disconnect();
});
userTimingObserver.observe({entryTypes: ["mark", "measure"]});
复制代码
另外有必要介绍一下 performanceObserver.observe
函数,它接受两个参数entryTypes
和buffered
。
- entryTypes 声明需要观察哪几类性能数据
- buffered 声明回调函数是立即同步执行还是异步执行,例子如下
const {
performance,
PerformanceObserver
} = require('perf_hooks');
const obs = new PerformanceObserver((list, observer) => {
// 同步执行三次. 每次`list` 仅包含一项 item.
});
obs.observe({ entryTypes: ['mark'] });
for (let n = 0; n < 3; n++)
performance.mark(`test${n}`);
复制代码
const {
performance,
PerformanceObserver
} = require('perf_hooks');
const obs = new PerformanceObserver((list, observer) => {
// 执行一次. `list` 包含3个 items.
});
obs.observe({ entryTypes: ['mark'], buffered: true });
for (let n = 0; n < 3; n++)
performance.mark(`test${n}`);
复制代码
总结
在Performance Timeline Level 2
规范中,扩充了performance
的定义,并增加了PerformanceObserver
的支持。
相比PerformanceObserver
,window.performance
在浏览器中兼容性较好,另外Performance Hooks
在 Node.js 中仍然处于 Experimental 阶段。
推荐在浏览器兼容的情况下使用PerformanceObserver
,使用这种观察者模式可以解决主动调用的问题,而且更为优雅。