优化 JavaScript 执行

JavaScript 经常会触发视觉变化。有时是直接通过样式操作,有时是会产生视觉变化的计算,例如搜索数据或将其排序。时机不当或长时间运行的 JavaScript 是导致性能问题的常见原因。您应当设法尽可能减少其影响。

Paul Lewis

JavaScript 经常会触发视觉变化。有时 样式设置,有时是计算 会导致视觉变化,例如搜索数据或对数据进行排序。时机不合适 或长时间运行的 JavaScript 是导致性能问题的常见原因。 您应当设法尽可能减少其影响。

JavaScript 性能分析可以说是一门艺术,因为您编写的 JavaScript 与实际执行的代码完全不同现代浏览器都使用 JIT 编译器 尽可能加快执行速度, 会显著改变代码的动态。

尽管如此,您还是可以采取一些措施来帮助您的应用 JavaScript。

摘要

  • 避免使用 setTimeout 或 setInterval 实现视觉更新;始终使用 requestAnimationFrame。
  • 将长时间运行的 JavaScript 从主线程移到 Web Worker。
  • 使用微任务来执行对多个帧的 DOM 更改。
  • 使用 Chrome 开发者工具的 Timeline 和 JavaScript Profiler 来评估 JavaScript 的影响。

使用 requestAnimationFrame 实现视觉变化

当屏幕上发生视觉变化时,您希望在合适的时间 浏览器,位于帧的开头。只有一种方法可以保证 将使用 requestAnimationFrame

/**
    * If run as a requestAnimationFrame callback, this
    * will be run at the start of the frame.
    */
function updateScreen(time) {
    // Make visual updates here.
}

requestAnimationFrame(updateScreen);

框架或示例可以使用 setTimeoutsetInterval 来执行视觉更改,例如动画、 但这种方法的问题在于,回调将在帧中的某个时间点运行,可能是 这种情况通常会导致我们遗漏一帧,进而导致卡顿。

setTimeout 导致浏览器丢失帧。

事实上,jQuery 过去使用 setTimeout 来实现其 animate 行为。它已更改为使用 requestAnimationFrame(在版本 3 中)。 如果您使用的是旧版 jQuery 进行修补以使用 requestAnimationFrame, 强烈建议您这样做

降低复杂性或使用 Web Worker

JavaScript 在浏览器的主线程上运行,与样式计算、布局以及 在许多情况下绘制如果您的 JavaScript 运行了很长时间,就会阻止这些其他任务, 可能会导致帧丢失

您应该灵活管理 JavaScript 的运行时间和运行时长。例如,如果您在 滚动之类的动画效果时,最好是想办法让 JavaScript 保持在 3-4 毫秒。如果超过这个时间,就可能占用太多时间。设备处于闲置状态时 那么对于所花费的时间,您可以更放松一些。

在许多情况下,可以将纯粹的计算工作 Web Workers, 。数据操纵或遍历 通常非常适合此模型。

var dataSortWorker = new Worker("sort-worker.js");
dataSortWorker.postMesssage(dataToSort);

// The main thread is now free to continue working on other things...

dataSortWorker.addEventListener('message', function(evt) {
    var sortedData = evt.data;
    // Update data on screen...
});

并非所有工作都适合这种模型:Web Worker 没有 DOM 访问权限。如果您的工作必须在主线程上完成,请考虑采用批处理方法,将较大的任务细分为多个微任务,每个微任务用时不超过几毫秒,并在每一帧的 requestAnimationFrame 处理程序中运行。

这种方法会对用户体验和界面产生影响,您需要确保用户知道 通过使用进度或活动指示器来表示正在处理任务。 在任何情况下,这种方法都能让应用的主线程处于空闲状态,从而帮助其保持对 用户互动

了解 JavaScript 的“帧税”

在评估框架、库或您自己的代码时,务必要评估 逐帧运行 JavaScript 代码所需的费用。这是 在执行对性能至关重要的动画工作(例如 过渡或滚动效果

Chrome DevTools 的“Performance”面板是衡量 JavaScript 的费用。通常,您会获得如下低级记录:

在 Chrome 开发者工具中记录性能

主要部分提供了 JavaScript 调用的火焰图,让您可以 可以准确地分析调用了哪些函数以及每个函数花费的时间。

有了这些信息,您就可以评估 JavaScript,并开始查找并修复 函数的执行时间过长。如前所述, 移除长时间运行的 JavaScript;如果无法移除, 从而释放主线程以继续处理其他任务。

请参阅开始分析运行时性能,了解如何使用 “性能”面板

避免微优化 JavaScript

知道浏览器执行某一版本的速度比执行版本快 100 倍可能很酷。 比方说,请求元素的 offsetTopgetBoundingClientRect(),但几乎总是这样,您只会调用如下函数: 因此在每帧中只处理很少次数,所以将精力通常浪费在这方面的工作上 JavaScript 的性能。您通常只能节省零点几毫秒的时间。

如果您正在开发游戏或计算成本高昂的应用,那么您可能属于例外情况, 因为您通常需要在单个帧中放置大量计算,并且 这样的话,一切都可以派上用场。

简而言之,您应对微优化非常谨慎,因为它们通常不会映射到 您正在构建的应用