JavaScript内核与高级编程之:`JavaScript`的`Performance API`:`performance.now()`和`PerformanceObserver`的用法。

嘿!大家好!我是老码农,今天咱来聊聊 JavaScript 里那些能让你代码跑得飞起的“秘密武器”——Performance API,特别是里面的 performance.now()PerformanceObserver 这俩哥们。

第一节:摸清底细,啥是 Performance API?

咱先别急着上代码,先搞清楚这 Performance API 到底是啥玩意儿。简单来说,它就是浏览器提供的一套工具,让你能够测量和分析你的网页或应用的性能。有了它,你就能知道哪些代码跑得慢,哪些地方需要优化,就像给你的代码做了个体检一样。

Performance API 提供了一系列接口,包括:

  • performance.now(): 这个是咱们今天要重点关注的,用来获取高精度的时间戳。
  • performance.mark()performance.measure(): 这俩哥们配合起来,可以让你标记代码中的特定点,然后测量这些点之间的时间差。
  • PerformanceObserver: 也是今天的主角之一,它能让你监听特定的性能事件,比如长任务(Long Tasks)等。
  • 还有一些其他的,比如 performance.timing (已废弃,不建议使用), performance.memory (非标准,各浏览器支持程度不一) 等,咱们今天先不细聊。

第二节:performance.now():时间都去哪儿了?

performance.now() 的作用很简单,就是返回一个高精度的时间戳,单位是毫秒。这个时间戳是相对于页面加载开始的时间的。注意,它不是绝对时间,而是相对时间,所以它不会受到系统时间的影响。

为啥要用 performance.now() 而不是 Date.now() 呢? 主要原因是精度更高。Date.now() 通常只能精确到毫秒级,而 performance.now() 可以精确到微秒级,甚至更高。 这在测量非常短的时间间隔时非常有用。

下面来个简单的例子:

const startTime = performance.now();

// 模拟一些耗时操作
for (let i = 0; i < 1000000; i++) {
    // do something
}

const endTime = performance.now();

const duration = endTime - startTime;

console.log(`这段代码执行了 ${duration} 毫秒`);

这个例子非常简单,就是测量一段循环代码的执行时间。通过 performance.now() 获取开始时间和结束时间,然后相减,就能得到代码的执行时间了。

第三节:performance.mark()performance.measure():精准定位性能瓶颈

performance.mark() 允许你在代码中标记特定的点,给它们起个名字。 然后 performance.measure() 可以测量两个标记点之间的时间差。

performance.mark('start');

// 模拟一些耗时操作
for (let i = 0; i < 1000000; i++) {
    // do something
}

performance.mark('end');

performance.measure('myMeasure', 'start', 'end');

const measure = performance.getEntriesByName('myMeasure')[0];

console.log(`'myMeasure' 这个过程耗时 ${measure.duration} 毫秒`);

在这个例子中,我们分别在循环开始和结束的地方打了两个标记,分别是 ‘start’ 和 ‘end’。 然后使用 performance.measure() 创建了一个名为 ‘myMeasure’ 的测量,指定了开始和结束的标记。 最后,我们使用 performance.getEntriesByName() 获取了 ‘myMeasure’ 的测量结果,并打印了它的持续时间。

你也可以在浏览器的 Performance 面板中看到这些标记和测量结果,能更直观地分析性能。

第四节:PerformanceObserver:实时监控性能指标

PerformanceObserver 允许你监听特定的性能事件。当这些事件发生时,它会通知你。 这对于实时监控性能指标非常有用,比如长任务(Long Tasks),布局偏移(Layout Shifts)等。

首先,你需要创建一个 PerformanceObserver 实例,并指定要监听的事件类型。 然后,你需要调用 observe() 方法来开始监听。

const observer = new PerformanceObserver((list) => {
  list.getEntries().forEach((entry) => {
    console.log(`检测到 ${entry.entryType} 事件:`, entry);
    // 在这里处理性能事件,比如记录日志、发送报告等
  });
});

observer.observe({ entryTypes: ['longtask', 'layout-shift'] });

在这个例子中,我们创建了一个 PerformanceObserver 实例,并指定要监听 ‘longtask’ 和 ‘layout-shift’ 两种事件类型。当有长任务或布局偏移发生时,observer 会调用回调函数,并将事件信息传递给回调函数。

entryTypes 可以是以下值之一:

事件类型 描述
"longtask" 长任务。 指的是执行时间超过 50 毫秒的任务,通常会导致页面卡顿。
"layout-shift" 布局偏移。指的是页面上的元素在用户没有预期的情况下发生移动,会导致用户体验下降。
"mark" performance.mark() 创建的标记。
"measure" performance.measure() 创建的测量。
"resource" 资源加载事件。
"navigation" 导航事件。
"paint" 渲染事件。
"event" 用户事件,比如点击、键盘输入等。 需要设置eventFilter 才能使用。

你可以根据需要监听不同的事件类型,并在回调函数中处理这些事件。

第五节:实战演练:监控长任务

咱们来个更实际的例子,用 PerformanceObserver 监控长任务,并将它们记录到控制台。

const observer = new PerformanceObserver((list) => {
  list.getEntries().forEach((entry) => {
    console.warn(`发现长任务:`, entry);
    console.warn(`长任务的持续时间:${entry.duration} 毫秒`);
    console.warn(`长任务的起始时间:${entry.startTime} 毫秒`);
    console.warn(`长任务的 attribution:`, entry.attribution); // attribution 包含了导致长任务的脚本信息
  });
});

observer.observe({ entryTypes: ['longtask'] });

// 模拟一个长任务
function simulateLongTask() {
  const startTime = performance.now();
  while (performance.now() - startTime < 100) {
    // 阻塞主线程
  }
}

// 触发长任务
setTimeout(simulateLongTask, 1000);

在这个例子中,我们创建了一个 PerformanceObserver 实例,监听 ‘longtask’ 事件。当有长任务发生时,我们会在控制台打印长任务的信息,包括持续时间、起始时间和 attribution。

attribution 属性包含了导致长任务的脚本信息,可以帮助你找到导致性能问题的代码。

我们还模拟了一个长任务,它会阻塞主线程 100 毫秒。 当你运行这段代码时,你应该能在控制台看到长任务的信息。

第六节:高级用法:过滤性能事件

PerformanceObserver 还支持过滤性能事件,让你只监听你感兴趣的事件。 你可以使用 eventFilter 选项来过滤事件。

例如,你可以只监听特定类型的用户事件:

const observer = new PerformanceObserver((list) => {
  list.getEntries().forEach((entry) => {
    console.log(`检测到 ${entry.name} 事件:`, entry);
  });
});

observer.observe({
  type: 'event',
  eventFilter: ['click', 'keydown'], //只监听 click 和 keydown 事件
});

document.addEventListener('click', () => {
  console.log('click event');
});

document.addEventListener('keydown', () => {
  console.log('keydown event');
});

在这个例子中,我们只监听 ‘click’ 和 ‘keydown’ 事件。 当你点击页面或按下键盘时,observer 就会收到通知。

第七节:停止监听:disconnect() 方法

当你不再需要监听性能事件时,你可以调用 disconnect() 方法来停止监听。

const observer = new PerformanceObserver((list) => {
  // ...
});

observer.observe({ entryTypes: ['longtask'] });

// 在某个时候停止监听
observer.disconnect();

调用 disconnect() 方法后,observer 将不再收到任何性能事件的通知。

第八节:最佳实践和注意事项

  • 不要过度使用: PerformanceObserver 会增加页面的开销,所以不要过度使用。 只监听你真正需要的事件。
  • 在生产环境中谨慎使用: 在生产环境中,你应该只记录必要的性能数据,避免过度收集数据导致性能下降。
  • 使用抽样: 如果需要收集大量的性能数据,可以考虑使用抽样技术,只收集一部分数据。
  • 注意浏览器兼容性: 虽然 Performance API 的支持度已经很高了,但还是要注意浏览器兼容性。 可以使用 polyfill 来提供兼容性支持。
  • 结合开发者工具使用: Performance API 配合浏览器的开发者工具使用,效果更佳。 你可以在开发者工具的 Performance 面板中查看更详细的性能数据。
  • 避免阻塞主线程:PerformanceObserver 的回调函数中,尽量避免执行耗时的操作,以免阻塞主线程。

第九节:总结

今天我们学习了 JavaScriptPerformance API,重点介绍了 performance.now()PerformanceObserver 的用法。 掌握这些工具,可以帮助你更好地了解和优化你的网页或应用的性能,让你的代码跑得更快、更流畅。

记住,性能优化是一个持续的过程,需要不断地学习和实践。 希望今天的分享对你有所帮助!

好了,今天的讲座就到这里。 感谢大家的聆听,咱们下次再见! (挥手)

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注