performance的定义
javascript
function _define() {
// Identify browser environment when following property is not present
// https://nodejs.org/dist/latest-v16.x/docs/api/perf_hooks.html#performancenodetiming
if (typeof performance === 'object' && typeof performance.mark === 'function' && !performance.nodeTiming) {
// in a browser context, reuse performance-util
if (typeof performance.timeOrigin !== 'number' && !performance.timing) {
// safari & webworker: because there is no timeOrigin and no workaround
// we use the `Date.now`-based polyfill.
return _definePolyfillMarks();
} else {
// use "native" performance for mark and getMarks
return {
mark(name) {
performance.mark(name);
},
getMarks() {
let timeOrigin = performance.timeOrigin;
if (typeof timeOrigin !== 'number') {
// safari: there is no timerOrigin but in renderers there is the timing-property
// see https://bugs.webkit.org/show_bug.cgi?id=174862
timeOrigin = performance.timing.navigationStart || performance.timing.redirectStart || performance.timing.fetchStart;
}
const result = [{ name: 'code/timeOrigin', startTime: Math.round(timeOrigin) }];
for (const entry of performance.getEntriesByType('mark')) {
result.push({
name: entry.name,
startTime: Math.round(timeOrigin + entry.startTime)
});
}
return result;
}
};
}
} else if (typeof process === 'object') {
// node.js: use the normal polyfill but add the timeOrigin
// from the node perf_hooks API as very first mark
const timeOrigin = performance?.timeOrigin ?? Math.round((require.__$__nodeRequire || require)('perf_hooks').performance.timeOrigin);
return _definePolyfillMarks(timeOrigin);
} else {
// unknown environment
console.trace('perf-util loaded in UNKNOWN environment');
return _definePolyfillMarks();
}
}
这段代码是由三个分支组成的,分别是三个条件:
- 浏览器环境,存在
performance
对象。 node
环境- 其他未知环境
其中第二个分支和第三个分支都是走_definePolyfillMarks
来返回结果。
在浏览器环境中,又判断了timeOrigin
的情况,主要是safari
和webworker
的兼容,也会降级到_definePolyfillMarks
。其他正常的情况下,会返回两个函数:
ini
{
mark(name) {
performance.mark(name);
},
getMarks() {
let timeOrigin = performance.timeOrigin;
if (typeof timeOrigin !== 'number') {
// safari: there is no timerOrigin but in renderers there is the timing-property
// see https://bugs.webkit.org/show_bug.cgi?id=174862
timeOrigin = performance.timing.navigationStart || performance.timing.redirectStart || performance.timing.fetchStart;
}
const result = [{ name: 'code/timeOrigin', startTime: Math.round(timeOrigin) }];
for (const entry of performance.getEntriesByType('mark')) {
result.push({
name: entry.name,
startTime: Math.round(timeOrigin + entry.startTime)
});
}
return result;
}
};
}
mark
就完全是performance.mark
的功能,然后getMarks
将返回一个数组,数组里面是两个属性:
name
,也就是mark
的name
startTime
,从performance
接口拿到的startTime
,再加上timeOrigin
,其实这个值就是时间戳了。
降级到_definePolyfillMarks
ini
function _definePolyfillMarks(timeOrigin) {
const _data = [];
if (typeof timeOrigin === 'number') {
_data.push('code/timeOrigin', timeOrigin);
}
function mark(name) {
_data.push(name, Date.now());
}
function getMarks() {
const result = [];
for (let i = 0; i < _data.length; i += 2) {
result.push({
name: _data[i],
startTime: _data[i + 1],
});
}
return result;
}
return { mark, getMarks };
}
这个降级函数非常简单,mark接口存入的是Date.now(),getMarks取出来的startTime就是当时存入的Date。
关于startTime和Date.now
startTime
是PerformanceEntry
对象的一个属性,表示开始时间,单位是毫秒。
Date.now()
是 JavaScript 的一个方法,返回当前时间的 Unix 时间戳,单位也是毫秒。Unix 时间戳是从 1970 年 1 月 1 日 00:00:00 UTC(协调世界时)到当前时间的总毫秒数。
startTime
和 Date.now()
的主要区别在于它们的参考点不同:
startTime
的参考点是performance.timeOrigin
,即性能时间线的起点。性能时间线是一个记录了所有性能相关事件(如标记和测量)的时间线。在大多数浏览器中,performance.timeOrigin
的值等于performance.timing.navigationStart
,即当前页面的导航开始时间。因此,startTime
表示的是从页面导航开始到创建性能标记的时间。Date.now()
的参考点是 Unix 纪元(1970 年 1 月 1 日 00:00:00 UTC)。因此,Date.now()
表示的是从 Unix 纪元到当前时间的总时间。
所以VSCode在这里使用performance.timeOrigin
加上startTime
,这个值就和Date.now()
是一个量级了。
小结一下
在VSCode
中,实现performance
模块非常简单,主要暴露mark
和getMarks
函数,用来标记和读取性能数据。
performance
是一个灵活的工具函数,根据不同的执行环境提供性能测量和调试工具的支持。它可以在浏览器环境和node.js
环境中使用原生的性能API,或者在其他未知环境中使用简单的降级函数来模拟性能标记的功能。