各位观众老爷们,大家好!今天咱们聊聊前端性能优化中一个非常实用的小工具——User Timing API
。这玩意儿就像一个精密的秒表,能帮你精确测量代码的执行时间,找到性能瓶颈,让你的网页跑得飞快!
咱们主要讲两个核心方法:performance.mark
和 performance.measure
。别害怕,它们的名字虽然听起来高大上,但用法非常简单,保证你一学就会!
Part 1: performance.mark
– 标记时间点
performance.mark
的作用就像你在赛道上设置一个个的计时点。它会在浏览器的性能时间轴上记录一个时间戳,这个时间戳代表着你代码执行过程中某个关键时刻。
用法:
performance.mark('开始加载数据'); // 标记数据加载的开始时间
// 一堆加载数据的代码...
performance.mark('数据加载完成'); // 标记数据加载的结束时间
简单吧?performance.mark()
接收一个字符串参数,这个字符串就是你给这个时间点起的名字,方便你以后查找和使用。
更深入一点:
performance.mark()
会创建一个 PerformanceMark
对象,这个对象会被添加到浏览器的性能时间轴上。你可以通过 performance.getEntriesByType('mark')
来获取所有标记的时间点。
performance.mark('开始加载数据');
performance.mark('数据加载完成');
const marks = performance.getEntriesByType('mark');
console.log(marks); // 输出一个数组,包含 '开始加载数据' 和 '数据加载完成' 两个 PerformanceMark 对象
每个 PerformanceMark
对象都包含以下属性:
name
: 你给这个时间点起的名字。entryType
: 固定为 "mark"。startTime
: 时间戳,表示这个时间点距离页面加载开始的时间(单位是毫秒)。duration
: 固定为 0,因为mark
只是一个时间点,没有时间跨度。
举个栗子:
假设你在一个页面中加载一个图片,你想知道图片加载的时间。
<!DOCTYPE html>
<html>
<head>
<title>Performance Mark Example</title>
</head>
<body>
<img id="myImage" src="your_image.jpg">
<script>
const image = document.getElementById('myImage');
image.onload = function() {
performance.mark('imageLoaded');
const marks = performance.getEntriesByType('mark');
console.log(marks);
};
performance.mark('imageLoadStart');
image.src = 'your_image.jpg'; // 确保重新设置 src 才能触发 onload 事件,如果是缓存图片,可能不会触发
</script>
</body>
</html>
在这个例子中,我们在图片加载开始前和加载完成后分别使用 performance.mark()
标记了时间点。当图片加载完成后,我们就可以通过 performance.getEntriesByType('mark')
获取这两个时间点,并计算图片加载的时间。
Part 2: performance.measure
– 计算时间间隔
有了 performance.mark
,我们就可以标记一系列的时间点。接下来,performance.measure
就能派上用场了,它可以计算两个时间点之间的时间间隔,告诉你代码执行了多久。
用法:
performance.mark('开始加载数据');
// 一堆加载数据的代码...
performance.mark('数据加载完成');
performance.measure('数据加载耗时', '开始加载数据', '数据加载完成');
performance.measure()
接收三个参数:
measureName
: 你给这次测量起的名字,方便你以后查找和使用。startMarkName
: 开始时间点的名字,必须是你之前用performance.mark()
标记过的。endMarkName
: 结束时间点的名字,同样必须是你之前用performance.mark()
标记过的。
更深入一点:
performance.measure()
会创建一个 PerformanceMeasure
对象,这个对象也会被添加到浏览器的性能时间轴上。你可以通过 performance.getEntriesByType('measure')
来获取所有测量结果。
performance.mark('开始加载数据');
performance.mark('数据加载完成');
performance.measure('数据加载耗时', '开始加载数据', '数据加载完成');
const measures = performance.getEntriesByType('measure');
console.log(measures); // 输出一个数组,包含 '数据加载耗时' 这个 PerformanceMeasure 对象
每个 PerformanceMeasure
对象都包含以下属性:
name
: 你给这次测量起的名字。entryType
: 固定为 "measure"。startTime
: 开始时间点的时间戳。duration
: 时间间隔,表示结束时间点和开始时间点之间的时间差(单位是毫秒)。
再举个栗子:
还是加载图片的例子,这次我们直接获取图片加载的时间。
<!DOCTYPE html>
<html>
<head>
<title>Performance Measure Example</title>
</head>
<body>
<img id="myImage" src="your_image.jpg">
<script>
const image = document.getElementById('myImage');
image.onload = function() {
performance.mark('imageLoaded');
performance.measure('imageLoadTime', 'imageLoadStart', 'imageLoaded');
const measures = performance.getEntriesByType('measure');
console.log(measures);
const imageLoadTime = measures[0].duration;
console.log(`图片加载耗时:${imageLoadTime} 毫秒`);
};
performance.mark('imageLoadStart');
image.src = 'your_image.jpg';
</script>
</body>
</html>
在这个例子中,我们使用 performance.measure()
计算了图片加载的时间,并将结果输出到控制台。
Part 3: performance.clearMarks
和 performance.clearMeasures
– 清理垃圾
随着你的代码越来越复杂,你可能会创建大量的 PerformanceMark
和 PerformanceMeasure
对象。为了避免内存泄漏,你可以使用 performance.clearMarks()
和 performance.clearMeasures()
来清理这些对象。
用法:
performance.clearMarks('开始加载数据'); // 清理名为 '开始加载数据' 的 mark
performance.clearMarks(); // 清理所有 mark
performance.clearMeasures('数据加载耗时'); // 清理名为 '数据加载耗时' 的 measure
performance.clearMeasures(); // 清理所有 measure
performance.clearMarks()
和 performance.clearMeasures()
都可以接收一个字符串参数,表示要清理的对象的名字。如果不传参数,则会清理所有对象。
Part 4: performance.getEntries
– 获取所有性能条目
如果你想一次性获取所有类型的性能条目(包括 mark、measure、resource 等),可以使用 performance.getEntries()
方法。
用法:
const entries = performance.getEntries();
console.log(entries); // 输出一个数组,包含所有性能条目
performance.getEntries()
返回一个数组,包含所有类型的性能条目。你可以遍历这个数组,根据 entryType
属性来区分不同的条目类型。
Part 5: 应用场景与高级技巧
- 测量函数执行时间:
function myFunction() {
performance.mark('myFunctionStart');
// 一些代码...
performance.mark('myFunctionEnd');
performance.measure('myFunctionExecutionTime', 'myFunctionStart', 'myFunctionEnd');
const measures = performance.getEntriesByName('myFunctionExecutionTime')[0];
console.log(`myFunction execution time: ${measures.duration} ms`);
}
myFunction();
- 测量用户交互时间:
document.getElementById('myButton').addEventListener('click', function() {
performance.mark('buttonClickStart');
// 处理点击事件的代码...
performance.mark('buttonClickEnd');
performance.measure('buttonClickProcessingTime', 'buttonClickStart', 'buttonClickEnd');
const measures = performance.getEntriesByName('buttonClickProcessingTime')[0];
console.log(`Button click processing time: ${measures.duration} ms`);
});
- 结合
requestAnimationFrame
进行更精确的测量:requestAnimationFrame
能确保你的代码在浏览器下一次重绘之前执行,这对于测量动画性能非常有用。
function animate() {
performance.mark('animationFrameStart');
// 执行动画相关的代码...
performance.mark('animationFrameEnd');
performance.measure('animationFrameDuration', 'animationFrameStart', 'animationFrameEnd');
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
// 然后,你可以定期(比如每秒)获取并分析 animationFrameDuration 的平均值和最大值。
setInterval(() => {
const measures = performance.getEntriesByName('animationFrameDuration');
let totalDuration = 0;
for (const measure of measures) {
totalDuration += measure.duration;
}
const averageDuration = totalDuration / measures.length;
console.log(`Average animation frame duration: ${averageDuration.toFixed(2)} ms`);
performance.clearMeasures('animationFrameDuration'); // 清除旧的测量数据
}, 1000);
- 使用
performance.now()
进行更细粒度的测量:performance.now()
返回一个高精度的时间戳,可以用于测量非常短的时间间隔,比如几微秒。 虽然performance.mark
和performance.measure
内部也是基于performance.now()
,但直接使用它可以让你更灵活地控制测量的开始和结束。
const startTime = performance.now();
// 一些非常耗时的代码...
const endTime = performance.now();
const duration = endTime - startTime;
console.log(`代码执行时间:${duration.toFixed(4)} ms`);
Part 6: 兼容性与注意事项
User Timing API
的兼容性非常好,几乎所有现代浏览器都支持。performance.mark
和performance.measure
的名字必须是唯一的,否则会覆盖之前的标记或测量。- 不要过度使用
User Timing API
,过多的标记和测量会影响性能。只在需要精确测量的地方使用。 - 在生产环境中,你应该移除或者禁用
User Timing API
的代码,因为它们会增加代码的体积,并且可能会暴露敏感信息。可以使用构建工具(如 Webpack 或 Parcel)来移除这些代码。 performance.now()
返回的时间戳是相对于performance.timeOrigin
的,即页面加载开始的时间。performance.getEntriesByName()
可以根据名字获取性能条目。
Part 7: 总结
User Timing API
是一个非常强大的工具,可以帮助你精确测量代码的执行时间,找到性能瓶颈,并进行优化。记住,优化是一个持续的过程,需要不断地测量、分析和改进。
方法/属性 | 作用 | 参数 | 返回值 |
---|---|---|---|
performance.mark(name) |
标记一个时间点 | name : 时间点的名字 (字符串) |
undefined |
performance.measure(name, startMark, endMark) |
计算两个时间点之间的时间间隔 | name : 测量结果的名字 (字符串), startMark : 开始时间点的名字 (字符串), endMark : 结束时间点的名字 (字符串) |
undefined |
performance.clearMarks(name) |
清除指定名字的 mark,不指定则清除所有 mark | name : 要清除的 mark 的名字 (字符串, 可选) |
undefined |
performance.clearMeasures(name) |
清除指定名字的 measure,不指定则清除所有 measure | name : 要清除的 measure 的名字 (字符串, 可选) |
undefined |
performance.getEntriesByType(type) |
获取指定类型的性能条目 | type : 条目类型 (字符串, 例如: "mark", "measure", "resource") |
一个数组,包含所有指定类型的性能条目 |
performance.getEntriesByName(name) |
获取指定名字的性能条目 | name : 条目的名字 (字符串) |
一个数组,包含所有指定名字的性能条目 |
performance.getEntries() |
获取所有性能条目 | 无 | 一个数组,包含所有性能条目 |
performance.now() |
返回一个高精度的时间戳,表示当前时间距离页面加载开始的时间(单位是毫秒),精度可达微秒级别。 | 无 | 一个浮点数,表示当前时间距离页面加载开始的时间(单位是毫秒) |
希望今天的讲解对大家有所帮助! 祝大家写出高性能的代码,告辞!