随着我们构建的网站越来越依赖 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 的高级优化和库修剪插件,例如 lodash-babel-plugin 或 webpack 的 ContextReplacementPlugin,用于像 Moment.js 这样的库。
- 缓存代码以最大限度地减少网络行程。
解析/编译
下载后,JavaScript 最繁重的成本之一是 JS 引擎解析/编译此代码的时间。在 Chrome DevTools 中,解析和编译是 Performance 面板中黄色“脚本”时间的一部分。
“自下而上”和“调用树”选项卡会显示确切的解析/编译时间

但是,这有什么关系呢?
花费大量时间解析/编译代码会严重延迟用户与您的网站进行交互的时间。您发送的 JavaScript 越多,在您的网站可交互之前解析和编译它所需的时间就越长。
按字节计算,对于浏览器来说,处理 JavaScript 比处理同等大小的图像或 Web 字体更昂贵 — 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(前 50 万个网站)来分析 移动设备上的 JavaScript 状态,我们可以看到 50% 的网站需要超过 14 秒才能实现交互。这些网站最多花费 4 秒时间来解析和编译 JS。
考虑到获取和处理 JS 和其他资源所需的时间,用户在感觉页面可以使用之前可能需要等待一段时间也就不足为奇了。我们绝对可以在这方面做得更好。
从您的页面中删除非关键 JavaScript 可以减少传输时间、CPU 密集型解析和编译以及潜在的内存开销。这也有助于更快地使您的页面具有交互性。
执行时间
不仅仅是解析和编译会产生成本。JavaScript 执行(解析/编译代码后运行代码)是在主线程上必须执行的操作之一。长时间的执行时间也会推迟用户与您的网站进行交互的时间。
如果脚本执行时间超过 50 毫秒,则交互时间将被下载、编译和执行 JS 所需的全部时间延迟 — Alex Russell
为了解决这个问题,JavaScript 受益于分成小块,以避免锁定主线程。探索是否可以减少执行期间完成的工作量。
其他成本
JavaScript 可能会以其他方式影响页面性能
- 内存。由于 GC(垃圾回收),页面可能会频繁出现卡顿或暂停。当浏览器回收内存时,JS 执行会暂停,因此频繁收集垃圾的浏览器可能会比我们希望的更频繁地暂停执行。避免 内存泄漏 和频繁的 GC 暂停,以保持页面流畅。
- 在运行时,长时间运行的 JavaScript 可能会阻塞主线程,导致页面无响应。将工作分解成更小的块(使用
requestAnimationFrame()
或requestIdleCallback()
进行调度)可以最大限度地减少响应性问题,这可以帮助改进 交互到下次绘制 (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 的“您能负担得起吗?:真实世界的 Web 性能预算”,以获取有关移动设备预算的指导。

如果您正在构建一个针对移动设备的网站,请尽最大努力在具有代表性的硬件上进行开发,保持您的 JavaScript 解析/编译时间较低,并采用性能预算来确保您的团队能够密切关注他们的 JavaScript 成本。
了解更多
- Chrome 开发者峰会 2017 - 现代加载最佳实践
- JavaScript 启动性能
- 解决 Web 性能危机 — Nolan Lawson
- 您能负担得起吗?真实世界的性能预算 — Alex Russell
- 评估 Web 框架和库 — Kristofer Baxter
- Cloudflare 的 Brotli 实验结果 用于压缩(请注意,更高质量的动态 Brotli 可能会延迟初始页面渲染,因此请仔细评估。您可能需要静态压缩)。
- 性能未来 — Sam Saccone