本文探讨了在瞬息万变的 JavaScript 生态系统中,如何优化 JavaScript 性能的诸多方面。我们将遵循“工具而非规则”的原则,尽量避免使用过多的 JavaScript 行话。由于篇幅有限,无法涵盖 JavaScript 性能优化的所有方面,请务必阅读参考链接并自行深入研究。
在深入探讨细节之前,让我们先从更宏观的角度来理解这个问题:什么是高性能 JavaScript,它如何与更广泛的 Web 性能指标相适应?
关键要点
背景设置
首先,我们需要明确一点:如果您只在台式机上进行测试,那么您就排除了超过 50% 的用户。
随着新兴市场用户主要通过价格低于 100 美元的 Android 设备访问互联网,这一趋势只会继续发展。台式机作为访问互联网的主要设备的时代已经结束,下一批十亿互联网用户将主要通过移动设备访问您的网站。
在 Chrome DevTools 的设备模式下进行测试并不能替代在真实设备上进行测试。使用 CPU 和网络限流有所帮助,但它本质上是不同的。请在真实设备上进行测试。
即使您正在在真实的移动设备上进行测试,您可能也在使用您全新的 600 美元旗舰手机进行测试。问题是,这并不是您的用户所拥有的设备。中位数设备类似于 Moto G1——这是一款内存小于 1GB、CPU 和 GPU 非常弱的设备。
让我们看看它在解析平均 JS 包时的表现如何。
Addy Osmani:JS 解析和评估的平均时间。
糟糕。虽然此图像仅涵盖 JS 的解析和编译时间(稍后将详细介绍),而不是整体性能,但它与整体性能密切相关,可以作为整体 JS 性能的指标。
引用 Bruce Lawson 的话:“这是万维网,而不是富裕的西方网络”。因此,您的 Web 性能目标是速度比您的 MacBook 或 iPhone 慢约 25 倍的设备。好好想想这一点。但情况更糟。让我们看看我们的实际目标是什么。
什么是高性能 JS 代码?
既然我们知道了目标平台,我们就可以回答下一个问题:什么是高性能 JS 代码?
虽然没有绝对的分类来定义高性能代码,但我们确实有一个以用户为中心的性能模型可以用作参考:RAIL 模型。
Sam Saccone:性能规划:PRPL
如果您的应用在 100 毫秒内响应用户操作,用户会认为响应是即时的。这适用于可点击的元素,但不适用于滚动或拖动。
在 60Hz 显示器上,我们希望在动画和滚动时达到每秒 60 帧的恒定帧率。这意味着每帧大约有 16 毫秒的时间。在这 16 毫秒的预算中,您实际上只有 8-10 毫秒的时间来完成所有工作,其余时间被浏览器内部机制和其他差异占用。
如果您有一个昂贵且持续运行的任务,请确保将其分割成更小的块,以便主线程能够响应用户输入。您不应该有一个任务会将用户输入延迟超过 50 毫秒。
您应该将页面加载时间控制在 1000 毫秒以内。超过这个时间,您的用户就会开始感到不耐烦。对于页面具有交互性而言,这是一个相当难以实现的目标,而不仅仅是将其绘制在屏幕上并可滚动。实际上,时间更短:
Fast By Default:现代加载最佳实践(2017 年 Chrome Dev 峰会)
实际上,目标是 5 秒的交互时间。这是 Chrome 在其 Lighthouse 审核中使用的指标。
既然我们知道了指标,让我们来看一些统计数据:
以及 Addy Osmani 提供的更多信息:
感觉够沮丧了吗?很好。让我们开始工作,修复 Web。✊
上下文至关重要
您可能已经注意到,主要瓶颈是加载网站所需的时间。具体来说,是 JavaScript 下载、解析、编译和执行时间。除了加载更少的 JavaScript 代码并更智能地加载之外,别无他法。
但是,除了启动网站之外,您的代码实际执行的工作呢?那里一定有一些性能提升,对吧?
在深入优化代码之前,请考虑您正在构建的内容。您是在构建框架还是 VDOM 库?您的代码是否需要每秒执行数千次操作?您是否正在为处理用户输入和/或动画编写一个时间关键型库?如果不是,您可能需要将时间和精力转移到更有效的地方。
这并不是说编写高性能代码不重要,而是它通常在整体情况中几乎没有影响,尤其是在谈论微优化时。因此,在您开始在 Stack Overflow 上就 .map 与 .forEach 与 for 循环进行争论,并比较 JSperf.com 上的结果之前,请确保您看到了森林,而不仅仅是树木。在纸面上,50k ops/s 可能听起来比 1k ops/s 好 50 倍,但在大多数情况下,它不会产生任何区别。
解析、编译和执行
从根本上说,大多数非高性能 JS 的问题不是运行代码本身,而是在代码开始执行之前必须执行的所有步骤。
我们在这里谈论的是抽象级别。您计算机中的 CPU 运行机器码。您计算机上运行的大部分代码都采用编译后的二进制格式。(考虑到当今所有的 Electron 应用程序,我说的是代码而不是程序。)这意味着,除了所有操作系统级别的抽象之外,它都在您的硬件上原生运行,无需任何准备工作。
JavaScript 没有预编译。它(通过相对较慢的网络)以可读代码的形式到达您的浏览器中,就所有意图和目的而言,它是您的 JS 程序的“操作系统”。
该代码首先需要进行解析——也就是说,读取并转换为可供计算机索引的结构,以便用于编译。然后将其编译成字节码,最后编译成机器码,然后才能由您的设备/浏览器执行。
另一个非常重要的事情需要提到的是,JavaScript 是单线程的,并且在浏览器的主线程上运行。这意味着一次只能运行一个进程。如果您的 DevTools 性能时间轴充满了黄色峰值,使您的 CPU 运行到 100%,您将遇到长时间/丢帧、卡顿滚动以及其他各种讨厌的事情。
Paul Lewis:当一切都很重要时,什么都不重要!。
因此,在您的 JS 开始工作之前,需要完成所有这些工作。在 Chrome 的 V8 引擎中,解析和编译最多占 JS 执行总时间的 50%。
Addy Osmani:JavaScript 启动性能。
您应该从本节中记住两件事:
有一些方法可以减轻这种情况,例如使用服务工作线程在后台和另一个线程上执行作业,使用 asm.js 编写更容易编译成机器指令的代码,但这又是另一个主题。
但是,您可以做的是避免为所有内容都使用 JS 动画框架,并了解触发绘制和布局的内容。仅当绝对无法使用常规 CSS 过渡和动画来实现动画时,才使用这些库。
即使它们可能使用 CSS 过渡、合成属性和 requestAnimationFrame(),它们仍在 JS 中、在主线程上运行。它们基本上只是每 16 毫秒都用内联样式来敲打您的 DOM,因为它们几乎没有其他事情可做。为了保持动画流畅,您需要确保所有 JS 都能在每帧 8 毫秒内完成执行。
另一方面,CSS 动画和过渡在主线程之外运行——如果高效地实现,则在 GPU 上运行,而不会导致重新布局/重新绘制。
考虑到大多数动画是在加载或用户交互期间运行的,这可以为您的 Web 应用提供急需的喘息空间。
Web Animations API 是一个即将推出的功能集,它允许您在主线程之外执行高性能 JS 动画,但目前,请坚持使用 CSS 过渡和 FLIP 等技术。
包大小至关重要
如今,一切都是关于包的。Bower 和在结束
以上是JavaScript性能优化提示:概述的详细内容。更多信息请关注PHP中文网其他相关文章!