[翻译]JavaScript的成本

1,180 阅读9分钟

当我们建立严重依赖于JavaScript的网站时,我们不总是很容易看到我们发送的内容所付出的代价。在这篇文章中,如果你希望你的网站在移动设备上能够快速加载和互动,我将介绍为什么一些规则可以帮助你。

tl;dr:较少的代码=较少的解析/编译+较少的传输+较少的解压缩

网络

当大多数开发人员考虑JavaScript的成本时,他们会考虑下载和执行成本。通过线路发送更多字节的JavaScript需要的时间越长,用户的连接也就越慢。

这可能是一个问题,即使在发达国家,因为用户有效的网络连接类型可能实际上并不是3G,4G或WiFi。你可能在咖啡店使用Wifi,但连接到的是2G的蜂窝热点。

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

  • 只发送用户需要的代码。代码拆分这时候就有用武之地了。

  • 压缩代码(ES515的 Uglify, ES2015的 babel- minify或uglify -es)

  • 高度压缩(使用Brotli〜q11,Zopfli或gzip的)。Brotli的压缩比超过Gzip。它让CertSimple节省了17%的JS压缩字节大小,LinkedIn节省了 4%的加载时间。

  • 删除未使用的代码。通过DevTools代码覆盖来确定。对于剥离的代码,可以查看tree shaking闭包编译器的高级优化和微调库插件如lodash-babel-plugin插件或WebPack的ContextReplacementPlugin如Moment.js库。使用babel-preset-env和browserlist来避免现代浏览器中已经存在的转译功能。高级开发人员可能会发现仔细分析Webpack捆绑包有助于识别修剪不需要的依赖关系。

  • 使用HTTP缓存以尽量减少网络跳转。确定脚本的最佳生命周期(max-age)和提供令牌验证(ETag)以避免传输没有变更的字节。Service Worker缓存可以使您的应用程序网络具有弹性,让您可以快速访问V8的代码缓存等功能。了解有关文件名哈希的长期缓存。

    如何减少发送给用户的JavaScript最佳实践

解析/编译

一旦JS下载完毕,JavaScript最大的一个成本就是JS引擎解析/编译这段代码的时间。在Chrome DevTools中,解析和编译是“性能”面板中黄色“脚本”时间的一部分。

Bottom-Up/Call Tree允许查看确切的解析/编译时序:

Chrome DevTools“性能”面板>自下而上。通过启用V8的运行时调用统计,我们可以看到分析和编译阶段花费的时间

但是,为什么这会有问题呢?

花费很长时间解析/编译代码会严重延迟用户与网站互动的时间。发送的JavaScript越多,在网站交互之前解析和编译的时间就越长。

同样多的字节,浏览器处理JavaScript要比同等大小的图像或Web字体开销更大。 - Tom Dale

与JavaScript相比,在处理等效大小的图片时(涉及到大量的图片仍然需要解码!),涉及到大量的开销,但是在一般的移动设备上,JS更有可能对页面的交互性产生负面影响。

JavaScript和图像字节的开销有很大的不同。图像通常不会阻塞主线程,也不会阻止接口在解码和栅格化时进行的交互。然而JS由于解析,编译和执行的成本会延迟交互性。

当我们谈论解析和编译变慢的时候,上下文很重要 - 我们在这里谈论的普通手机。指的是大部分用户使用的CPU和GPU速度较慢的手机,无L2 / L3缓存,甚至可能内存很小

网络功能和设备功能并不总是相匹配。使用光纤连接的用户不一定有最好的CPU来解析和执行发送到他们的设备的JavaScript。反过来也是如此。一个糟糕的网络连接,但却有一个快速的CPU。 - Kristofer Baxter,LinkedIn

JavaScript启动性能中,我注意到在低端和高端硬件上解析大约1MB已经被解压的(简单)JavaScript的开销。在市场上最快的手机和普通手机之间解析/编译代码的时间有2-5倍的差异

在不同类别的桌面和移动设备上解析一个1MB的JavaScript包(约有250KB 被gzip压缩了)的解析时间。在查看解析的开销时,解压后的数据要考虑到大约有250KB 被gzip压缩的空间会被释放出来当解压缩大约1MB的代码的时候

那么现实世界的网站如何呢,如CNN.com 在高端的iPhone 8上,解析/编译CNN的JS需要花费大约4秒,而普通手机(Moto G4)则只需要13秒。这可以显著影响用户与本网站完全交互的速度。

苹果的A11仿生芯片和非常普通的Android硬件中的Snapdragon 617的解析时间性能比较。

这突出了普通硬件(如Moto G4)测试的重要性,而不仅仅是口袋里的手机。但是,使用环境很重要:优化您的用户拥有的设备和网络条件。

分析可以深入了解您的真实用户访问您的网站的移动设备类别。这可以提供机会来了解他们正在使用的真正的CPU / GPU的限制。

我们是否真的发送了太多的JavaScript?错误,这很有可能:)

使用HTTP Archive(最高大约50万个站点)来分析移动设备上JavaScript的状态,我们可以看到,50%的站点需要14秒才能获得交互。仅仅只是解析和编译JS,这些网站就花费长达4秒。

在获取和处理JS和其他资源所花费的时间中,因为感觉网页已经可以使用,用户可能会等待一段时间,这也许并不奇怪。但我们一定可以在这里做得更好。

从网页中删除不重要的JavaScript可以减少传输时间,CPU密集型解析和编译以及潜在的内存开销。这也有助于让您的网页更快地交互。

执行时间

这不仅仅是解析和编译,而且可能会带来额外的开销。JavaScript执行(一次解析/编译运行代码)是在主线程上发生的操作之一。很长的执行时间也可以推出用户可以与网站互动的时间。

如果脚本执行时间超过50ms,则交互时间会被下载,编译和执行JS所花时间延迟了 - Alex Russell

为了解决这个问题,JavaScript受益于small chunks,以避免锁定主线程。探索是否可以减少执行过程中正在进行的工作量。

减少JavaScript发送开销的模式

当你试图保持JavaScript的解析/编译和网络传输时间很慢时,有一些模式可以帮助像基于路由的分块(route-based chunking)或PRPL

PRPL是一种通过代码分割和缓存来优化交互性的模式:

让我们看看它可以产生的影响。 我们使用V8的运行时调用统计来分析流行移动网站和Progressive Web Apps的加载时间。正如我们所看到的,解析时间(以橙色显示)是许多这些站点花费时间的比较大的部分:

Wego是一个使用PRPL的网站,它设法保持较低的路由解析时间,能够非常迅速地进行互动。上面的许多其他站点都采用代码分解和性能预算来降低JS的开销。

其他开销

JavaScript可以通过其他方式影响页面性能:

  • 内存。由于GC(垃圾收集),页面可能经常会出现卡顿或暂停。当浏览器回收内存时,JS执行被暂停,所以经常进行垃圾收集的浏览器可以比我们想要的更频繁地暂停执行。避免内存泄漏和频繁的gc暂停,以保持页面流畅。

  • 在运行时,长时间运行的JavaScript可以阻止主线程导致无响应的页面。将工作分成更小的部分(使用requestAnimationFrame()或有计划的使用requestIdleCallback())可以最大限度地减少响应性问题。

渐进式引导

许多网站将内容可视性作为交互性的代价来优化。为了在有大的JavaScript包时获得一个快速的首页,开发者有时会使用服务器端渲染;然后在JavaScript最终获取时将其“升级”以附加事件处理程序。

小心 - 这有它自己的开销。1)这通常会发送一个更大的 HTML响应,这会延迟交互性,2)这会把用户留在一个离奇的山谷中,其中有一半的实际功能是没有办法交互的,直到JavaScript运行完成。

渐进式引导可能是一个更好的方法。发送一个最小功能的页面(由当前路由所需的HTML / JS / CSS组成)。随着更多的资源到达,该应用程序可以延迟加载和解锁更多的功能。

Paul Lewis的渐进式引导视觉

根据所看到的加载代码是圣杯。PRPL和渐进引导是可以帮助实现这一点的模式。

结论

传输大小对低端网络至关重要。解析时间对于CPU受限的设备很重要。保持这两者的抵开销。

团队发现成功采用严格的性能预算来保持JavaScript传输和解析/编译时间较短。请参阅Alex Russell的“ 您可以承受吗?:真实世界的Web性能预算 ”,以获取关于移动预算的指导。

考虑一下我们制作的架构决策可以为应用程序逻辑留下多少JS“空间”。

如果您正在构建针对移动设备的网站,请尽可能在代表性硬件上开发,保持较低的JavaScript分析/编译时间,并采用性能预算来确保您的团队能够关注其JavaScript成本。

原文地址