JavaScript 通常会触发视觉变化。有时,是直接通过样式操作实现的,有时则是通过计算(例如搜索或排序数据)实现的。时间安排不当或长时间运行的 JavaScript 是导致性能问题的常见原因。您应尽可能减少其影响。
JavaScript 通常会触发视觉更改。有时,是直接通过样式操作实现的,有时则是通过计算(例如搜索或排序数据)实现的。时间安排不当或长时间运行的 JavaScript 是导致性能问题的常见原因。您应尽可能减少其影响。
JavaScript 性能分析可以说是一门艺术,因为您编写的 JavaScript 与实际执行的代码完全不同。现代浏览器使用 JIT 编译器以及各种优化和技巧,力求尽可能快速执行,这会显著改变代码的动态性。
尽管如此,您还是可以采取一些措施来帮助应用顺利执行 JavaScript。
摘要
- 避免使用 setTimeout 或 setInterval 进行视觉更新;请始终改用 requestAnimationFrame。
- 将长时间运行的 JavaScript 从主线程移至 Web Worker。
- 使用微任务在多个帧中进行 DOM 更改。
- 使用 Chrome 开发者工具的时间轴和 JavaScript 性能分析器来评估 JavaScript 的影响。
使用 requestAnimationFrame
进行视觉更改
当屏幕上发生视觉变化时,您希望在浏览器的正确时间执行工作,即在帧开始时。若要确保 JavaScript 会在帧开始时运行,唯一的方法是使用 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);
框架或示例可以使用 setTimeout
或 setInterval
进行动画等视觉更改,但问题在于回调将在帧的某个时间点运行,可能就在帧结束时运行,这通常会导致我们错过帧,从而导致卡顿。

事实上,jQuery 过去使用 setTimeout
来实现 animate
行为。在版本 3 中,它已更改为使用 requestAnimationFrame
。如果您使用的是旧版 jQuery,可以对其进行补丁以使用 requestAnimationFrame
,我们强烈建议您这样做。
降低复杂性或使用 Web Worker
JavaScript 在浏览器的主线程上运行,与样式计算、布局以及在许多情况下绘制操作一起运行。如果 JavaScript 运行时间过长,会阻塞这些其他任务,可能会导致帧丢失。
您应有策略地决定 JavaScript 的运行时间和时长。例如,如果您正在执行滚动等动画,理想情况下,您应该将 JavaScript 的执行时间控制在 3-4 毫秒左右。超过这个时间,就可能会占用太多时间。如果您处于空闲期,则可以对所需时间放宽要求。
在许多情况下,如果纯计算工作不需要访问 DOM,您可以将其移至 Web Worker。数据操作或遍历(例如排序或搜索)通常非常适合此模型,加载和模型生成也是如此。
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 代码的开销。在执行对性能至关重要的动画工作(例如转换或滚动)时,这一点尤为重要。
若要衡量 JavaScript 的开销,Chrome 开发者工具的“性能”面板是最佳方式。通常,您会收到如下低级记录:

Main 部分会提供 JavaScript 调用的火焰图,以便您准确分析调用了哪些函数以及每个函数花了多长时间。
有了这些信息,您就可以评估 JavaScript 对应用性能的影响,并开始查找并修复函数执行时间过长的所有热点。如前所述,您应尝试移除长时间运行的 JavaScript,如果无法移除,则将其移至 Web Worker,以释放主线程来继续执行其他任务。
如需了解如何使用“性能”面板,请参阅开始分析运行时性能。
避免对 JavaScript 进行微优化
得知浏览器可以比另一个版本更快地执行某个操作(例如,请求元素的 offsetTop
比计算 getBoundingClientRect()
更快)可能很酷,但几乎总是如此,您每帧只会调用这些函数几次,因此通常没必要专注于 JavaScript 性能的这一方面。通常,您只会节省几毫秒的时间。
如果您要制作游戏或计算开销较大的应用,则可能属于此指南的例外情况,因为您通常需要将大量计算放入单个帧中,在这种情况下,一切都很有帮助。
简而言之,您应非常谨慎地使用微优化,因为它们通常与您构建的应用类型不符。