随着网站越来越依赖 JavaScript,我们有时会以不易察觉的方式为发送的内容付出代价。在本文中,我们将介绍如果您希望自己的网站在移动设备上快速加载并能够快速互动,为什么需要稍加自律。传送的 JavaScript 越少,网络传输时间、解压缩代码所花时间以及解析和编译此 JavaScript 所花时间就越短。
网络
大多数开发者在考虑 JavaScript 费用时,都会从下载和执行费用的角度来考虑。用户的连接速度越慢,通过网络发送的 JavaScript 字节数越多,所需时间就越长。
这可能会造成问题,因为用户的有效网络连接类型可能实际上并非 3G、4G 或 Wi-Fi。您可以连接到咖啡馆的 Wi-Fi,但连接到移动热点的速度为 2G。
您可以通过以下方式降低 JavaScript 的网络传输费用:
- 仅发送用户需要的代码。
- 缩减
- 使用 UglifyJS 缩减 ES5 代码。
- 使用 babel-minify 或 uglify-es 缩减 ES2015 及更高版本的大小。
- 压缩
- 移除未使用的代码。
- 使用 DevTools 代码覆盖率功能,找出可以移除或延迟加载的代码。
- 使用 babel-preset-env 和 browserlist 可避免转译现代浏览器中已有的功能。高级开发者可能会发现,仔细分析其 webpack 软件包有助于发现缩减不需要的依赖项的机会。
- 如需剥离代码,请参阅tree-shaking、Closure Compiler 的高级优化以及适用于 Moment.js 等库的 lodash-babel-plugin 或 webpack 的 ContextReplacementPlugin 等库修剪插件。
- 缓存代码以尽可能减少网络往返次数。
解析/编译
下载后,JavaScript 的最大开销之一是 JS 引擎解析/编译此代码所需的时间。在 Chrome DevTools 中,“解析”和“编译”属于“性能”面板中黄色“脚本”时间的一部分。
“自下而上”和“调用树”标签页会显示确切的解析/编译时间:

但为什么这很重要?
如果花费很长时间解析/编译代码,用户与您的网站互动的时间可能会大大延长。您发送的 JavaScript 越多,在网站变为交互状态之前,解析和编译所需的时间就越长。
与等大小的图片或 Web 字体相比,JavaScript 对浏览器的处理开销更高 - Tom Dale
与 JavaScript 相比,处理大小相同的图片需要付出诸多代价(它们仍然需要解码!),但在平均水平的移动硬件上,JS 更有可能对网页的交互性产生负面影响。

当我们谈到解析和编译速度缓慢时,背景信息非常重要,我们在这里所说的是在普通手机上运行。普通用户使用的手机可能具有运行缓慢的 CPU 和 GPU、没有 L2/L3 缓存,甚至可能存在内存限制。
网络功能和设备功能并不总是一致的。即使用户拥有出色的光纤连接,也未必拥有最强大的 CPU 来解析和评估发送到其设备的 JavaScript。反之亦然,网络连接很差,但 CPU 速度极快。- Kristofer Baxter,LinkedIn
下方显示了在低端和高端硬件上解析大约 1MB 的解压缩(简单)JavaScript 的开销。市场上最快的手机与普通手机在解析/编译代码时所需时间相差 2-5 倍。

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

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

我们真的发送了太多的 JavaScript 吗?呃,可能 :)
使用 HTTP Archive(约 5 万个热门网站)分析移动设备上的 JavaScript 状态后,我们发现 50% 的网站需要超过 14 秒才能实现交互。这些网站在解析和编译 JS 上最多需要花费 4 秒钟的时间。
考虑到提取和处理 JS 和其他资源所需的时间,因此用户可能需要等待一段时间才能使用页面,这也许并不奇怪。我们绝对可以做得更好。
从网页中移除非关键 JavaScript 可以缩短传输时间、减少 CPU 密集型解析和编译,并减少潜在的内存开销。这还有助于加快网页的互动速度。
执行时间
不仅解析和编译会产生费用,JavaScript 执行(在解析/编译后运行代码)是必须在主线程中执行的操作之一。执行时间过长也会延长用户与您的网站互动的时长。
如果脚本执行时间超过 50 毫秒,交互所需时间会延迟到下载、编译和执行 JS 所用的时间 - Alex Russell
为解决此问题,JavaScript 应分为小块,以避免锁定主线程。探索是否可以减少执行期间执行的工作量。
其他费用
JavaScript 还会以其他方式影响网页性能:
- 内存。由于 GC(垃圾回收),网页可能会出现卡顿或频繁暂停。当浏览器回收内存时,JS 执行会暂停,因此频繁收集垃圾的浏览器可能会比我们期望的更频繁地暂停执行。避免内存泄露和频繁的 GC 暂停,以确保网页不会出现卡顿。
- 在运行时,长时间运行的 JavaScript 可能会阻塞主线程,导致页面无响应。将工作拆分成更小的部分(使用
requestAnimationFrame()
或requestIdleCallback()
进行调度)可以最大限度地减少响应速度问题,这有助于改善 Interaction to Next Paint (INP)。
降低 JavaScript 提交费用的模式
如果您尝试让 JavaScript 的解析/编译和网络传输时间保持较慢的速度,可以使用一些模式来帮助您,例如基于路由的分块或 PRPL。
PRPL
PRPL(推送、呈现、预缓存、延迟加载)是一种通过积极的代码分块和缓存来优化互动性的模式:
我们来直观地了解一下这可能会产生怎样的影响。
我们使用 V8 的运行时调用统计信息来分析热门移动网站和渐进式 Web 应用的加载时间。如我们所见,解析时间(以橙色显示)是许多此类网站花费时间最多的部分:
Wego 是一个使用 PRPL 的网站,它设法将路线的解析时间保持在较低水平,从而能够非常快速地实现交互。上述许多其他网站都采用了代码分块和性能预算,以尝试降低其 JS 开销。
渐进式引导
许多网站在优化内容曝光度时,会牺牲互动性。为了在有大型 JavaScript 软件包时实现快速首次绘制,开发者有时会采用服务器端呈现;然后在最终提取 JavaScript 时“升级”它以附加事件处理脚本。
请注意,这样做会产生相应费用。您 1) 通常会发送更大的 HTML 响应,这可能会降低互动性;2) 可能会让用户陷入一种奇怪的体验,在 JavaScript 完成处理之前,用户体验的一半实际上无法互动。
渐进式引导可能是更好的方法。发送基本功能页面(仅包含当前路线所需的 HTML/JS/CSS)。随着更多资源的到来,应用可以延迟加载并解锁更多功能。

按视图中显示的内容加载代码是至关重要的。PRPL 和渐进式引导模式可以帮助实现这一点。
总结
传输大小对低端网络至关重要。解析时间对 CPU 受限设备非常重要。请务必将这些指标控制在较低水平。
一些团队通过采用严格的性能预算,成功地缩短了 JavaScript 传输和解析/编译时间。请参阅 Alex Russell 的“Can You Afford It?: ”一文,了解移动设备的预算指南。

如果您要构建面向移动设备的网站,请尽量在代表性硬件上进行开发,缩短 JavaScript 解析/编译时间,并采用性能预算,以确保您的团队能够密切关注 JavaScript 开销。
了解详情
- 2017 年 Chrome 开发者峰会 - 现代加载最佳实践
- JavaScript 启动性能
- 解决网络性能危机 - Nolan Lawson
- 您能否承担费用?实际效果预算 - Alex Russell
- 评估 Web 框架和库 - Kristofer Baxter
- Cloudflare 使用 Brotli 进行压缩实验的结果(请注意,质量较高的动态 Brotli 可能会延迟初始网页呈现,因此请谨慎评估。您可能需要改为静态压缩。)
- 效果 Futures - Sam Saccone