JavaScript 启动优化

随着网站越来越依赖 JavaScript,我们有时会以不易察觉的方式为发送的内容付出代价。在本文中,我们将介绍如果您希望自己的网站在移动设备上快速加载并能够快速互动,为什么需要稍加自律。传送的 JavaScript 越少,网络传输时间、解压缩代码所花时间以及解析和编译此 JavaScript 所花时间就越短。

网络

大多数开发者在考虑 JavaScript 费用时,都会从下载和执行费用的角度来考虑。用户的连接速度越慢,通过网络发送的 JavaScript 字节数越多,所需时间就越长。

当浏览器请求资源时,需要提取该资源,然后对其进行解压缩。对于 JavaScript 等资源,必须先解析和编译,然后才能执行。

这可能会造成问题,因为用户的有效网络连接类型可能实际上并非 3G、4G 或 Wi-Fi。您可以连接到咖啡馆的 Wi-Fi,但连接到移动热点的速度为 2G。

您可以通过以下方式降低 JavaScript 的网络传输费用:

  • 仅发送用户需要的代码
    • 使用代码拆分将 JavaScript 拆分为关键代码和非关键代码。webpack 等模块捆绑工具支持代码分块
    • 延迟加载非关键代码。
  • 缩减
  • 压缩
    • 至少应使用 gzip 压缩文本资源。
    • 考虑使用 Brotli 版本 ~q11。在压缩率方面,Brotli 的表现优于 gzip。这有助于 CertSimple 缩减压缩 JS 字节大小 17%,并帮助 LinkedIn 缩短加载时间 4%
  • 移除未使用的代码
  • 缓存代码以尽可能减少网络往返次数。
    • 使用 HTTP 缓存可确保浏览器有效缓存响应。确定脚本的最佳生命周期 (max-age) 并提供验证令牌 (ETag),以避免传输未更改的字节。
    • Service Worker 缓存可以使您的应用网络具有弹性,并让您能够提前访问 V8 的代码缓存等功能。
    • 使用长期缓存可避免重新提取未更改的资源。如果使用 Webpack,请参阅文件名哈希处理

解析/编译

下载后,JavaScript 的最大开销之一是 JS 引擎解析/编译此代码所需的时间。在 Chrome DevTools 中,“解析”和“编译”属于“性能”面板中黄色“脚本”时间的一部分。

ALT_TEXT_HERE

“自下而上”和“调用树”标签页会显示确切的解析/编译时间:

ALT_TEXT_HERE
Chrome 开发者工具“性能”面板 >“自下而上”。启用 V8 的运行时调用统计信息后,我们可以查看在解析和编译等阶段所花费的时间

但为什么这很重要?

ALT_TEXT_HERE

如果花费很长时间解析/编译代码,用户与您的网站互动的时间可能会大大延长。您发送的 JavaScript 越多,在网站变为交互状态之前,解析和编译所需的时间就越长。

与等大小的图片或 Web 字体相比,JavaScript 对浏览器的处理开销更高 - Tom Dale

与 JavaScript 相比,处理大小相同的图片需要付出诸多代价(它们仍然需要解码!),但在平均水平的移动硬件上,JS 更有可能对网页的交互性产生负面影响。

ALT_TEXT_HERE
JavaScript 和图片字节的开销差异很大。图片在解码和光栅化期间通常不会阻塞主线程或阻止界面进行交互。不过,由于解析、编译和执行开销,JS 可能会延迟互动性。

当我们谈到解析和编译速度缓慢时,背景信息非常重要,我们在这里所说的是在普通手机上运行。普通用户使用的手机可能具有运行缓慢的 CPU 和 GPU、没有 L2/L3 缓存,甚至可能存在内存限制。

网络功能和设备功能并不总是一致的。即使用户拥有出色的光纤连接,也未必拥有最强大的 CPU 来解析和评估发送到其设备的 JavaScript。反之亦然,网络连接很差,但 CPU 速度极快。- Kristofer Baxter,LinkedIn

下方显示了在低端和高端硬件上解析大约 1MB 的解压缩(简单)JavaScript 的开销。市场上最快的手机与普通手机在解析/编译代码时所需时间相差 2-5 倍

ALT_TEXT_HERE
此图表突出显示了不同类别的桌面设备和移动设备上 1MB 的 JavaScript 软件包(经过 GZIP 压缩后约为 250KB)的解析时间。在考虑解析开销时,请考虑解压缩后的数字,例如,大约 250KB 的压缩 JS 解压缩后会变成大约 1MB 的代码。

那么,CNN.com 这样的真实网站呢?

在高端 iPhone 8 上,解析/编译 CNN 的 JS 只需大约 4 秒,而普通手机(Moto G4)则需要大约 13 秒。这可能会显著影响用户与此网站完全互动的速度。

ALT_TEXT_HERE
上图比较了在更普通的 Android 硬件上,Apple 的 A11 Bionic 芯片与骁龙 617 的性能。

这凸显了在普通硬件(例如 Moto G4)上进行测试的重要性,而不仅仅是测试您可能放在口袋里的手机。不过,情境很重要:请根据用户的设备和网络条件进行优化

ALT_TEXT_HERE
Google Analytics 可以深入了解您的真实用户访问您网站时所用的移动设备类别。这样一来,您就可以了解应用在运行时所面临的实际 CPU/GPU 限制。

我们真的发送了太多的 JavaScript 吗?呃,可能 :)

使用 HTTP Archive(约 5 万个热门网站)分析移动设备上的 JavaScript 状态后,我们发现 50% 的网站需要超过 14 秒才能实现交互。这些网站在解析和编译 JS 上最多需要花费 4 秒钟的时间。

ALT_TEXT_HERE

考虑到提取和处理 JS 和其他资源所需的时间,因此用户可能需要等待一段时间才能使用页面,这也许并不奇怪。我们绝对可以做得更好。

从网页中移除非关键 JavaScript 可以缩短传输时间、减少 CPU 密集型解析和编译,并减少潜在的内存开销。这还有助于加快网页的互动速度。

执行时间

不仅解析和编译会产生费用,JavaScript 执行(在解析/编译后运行代码)是必须在主线程中执行的操作之一。执行时间过长也会延长用户与您的网站互动的时长。

ALT_TEXT_HERE

如果脚本执行时间超过 50 毫秒,交互所需时间会延迟到下载、编译和执行 JS 所用的时间 - Alex Russell

为解决此问题,JavaScript 应分为小块,以避免锁定主线程。探索是否可以减少执行期间执行的工作量。

其他费用

JavaScript 还会以其他方式影响网页性能:

  • 内存。由于 GC(垃圾回收),网页可能会出现卡顿或频繁暂停。当浏览器回收内存时,JS 执行会暂停,因此频繁收集垃圾的浏览器可能会比我们期望的更频繁地暂停执行。避免内存泄露和频繁的 GC 暂停,以确保网页不会出现卡顿。
  • 在运行时,长时间运行的 JavaScript 可能会阻塞主线程,导致页面无响应。将工作拆分成更小的部分(使用 requestAnimationFrame()requestIdleCallback() 进行调度)可以最大限度地减少响应速度问题,这有助于改善 Interaction to Next Paint (INP)

降低 JavaScript 提交费用的模式

如果您尝试让 JavaScript 的解析/编译和网络传输时间保持较慢的速度,可以使用一些模式来帮助您,例如基于路由的分块或 PRPL

PRPL

PRPL(推送、呈现、预缓存、延迟加载)是一种通过积极的代码分块和缓存来优化互动性的模式:

ALT_TEXT_HERE

我们来直观地了解一下这可能会产生怎样的影响。

我们使用 V8 的运行时调用统计信息来分析热门移动网站和渐进式 Web 应用的加载时间。如我们所见,解析时间(以橙色显示)是许多此类网站花费时间最多的部分:

ALT_TEXT_HERE

Wego 是一个使用 PRPL 的网站,它设法将路线的解析时间保持在较低水平,从而能够非常快速地实现交互。上述许多其他网站都采用了代码分块和性能预算,以尝试降低其 JS 开销。

渐进式引导

许多网站在优化内容曝光度时,会牺牲互动性。为了在有大型 JavaScript 软件包时实现快速首次绘制,开发者有时会采用服务器端呈现;然后在最终提取 JavaScript 时“升级”它以附加事件处理脚本。

请注意,这样做会产生相应费用。您 1) 通常会发送更大的 HTML 响应,这可能会降低互动性;2) 可能会让用户陷入一种奇怪的体验,在 JavaScript 完成处理之前,用户体验的一半实际上无法互动。

渐进式引导可能是更好的方法。发送基本功能页面(仅包含当前路线所需的 HTML/JS/CSS)。随着更多资源的到来,应用可以延迟加载并解锁更多功能。

ALT_TEXT_HERE
Paul Lewis 撰写的渐进式引导

按视图中显示的内容加载代码是至关重要的。PRPL 和渐进式引导模式可以帮助实现这一点。

总结

传输大小对低端网络至关重要。解析时间对 CPU 受限设备非常重要。请务必将这些指标控制在较低水平。

一些团队通过采用严格的性能预算,成功地缩短了 JavaScript 传输和解析/编译时间。请参阅 Alex Russell 的“Can You Afford It?: ”一文,了解移动设备的预算指南。

ALT_TEXT_HERE
不妨考虑我们所做的架构决策能为应用逻辑留出多少 JS“余量”。

如果您要构建面向移动设备的网站,请尽量在代表性硬件上进行开发,缩短 JavaScript 解析/编译时间,并采用性能预算,以确保您的团队能够密切关注 JavaScript 开销。

了解详情