JS 的重量
JS 的权重
章节
衡量 JS 性能的主要指标有两个。第一个指标备受瞩目,那就是 DOM 更新,现代框架几乎已经将其发挥到极致。它实际上取决于应用程序代码,而且通常对你的应用程序来说并不重要(当你的后端花了 2 秒来收集数据时,谁会关心几毫秒的渲染性能呢?)。
对于第二个问题,框架通常甚至不会提供实际数据,而是隐藏在诸如服务器端渲染 (SSR)、Tree Shaking 或延迟加载等旨在解决问题的功能背后。然而,在很多情况下,它比 JS 应用中的任何其他问题都更重要。
它是你的 JavaScript 代码的重量。
永远不要忘记这样一个事实:即使我们将如今的网站视为功能齐全的应用程序,Web 仍然是一个平台,当你打开一个标签页时,整个应用程序都会被下载、编译和启动。想象一下 Outlook、Word 或 Photoshop 的情形!
Web 应用的加载首先需要网络带宽来下载文件,然后 CPU 负责解压和编译。代码越多,应用所需的带宽和 CPU 就越多。即便如此,代码越多最终也意味着应用速度变慢。看看任天堂的这条推文就知道了。
他们几乎不需要考虑网络问题(据我所知),但他们非常关心应用程序的大小以及性能和流畅度。
最后:“代码越少,bug 就越少”。我一直很喜欢这句话。交付更少的代码(包括你自己的和第三方的)对稳定性总是有好处的。
工具并不能解决所有问题
如今我们使用的 Web 工具种类繁多,很大一部分功能都是为了限制 JS 代码的大小。所有这些优化技术都非常棒,我建议几乎全部使用。然而,优化并非解决方案,JS 代码的实际大小仍然是一个问题。
捆绑
解决 JS 性能问题时,我们使用的第一个工具是打包。我们不会向浏览器提供与开发时一样多的文件,因为我们知道每个服务器请求都会产生开销。大多数情况下,我们会编译一个包含 1、2 到 5 个打包 JS 文件的包。然而,你的代码量仍然和以前一样多。
最小化
长期以来,我们一直在使用 JS 压缩技术,这意味着修改代码时会删除大部分空格和换行符,并将变量重命名为更短的名称,以便在代码几乎相同的情况下使用更少的字符,从而获得更小的体积。这种方法非常棒,除了可读性和可调试性(可以通过源码映射来解决)之外几乎没有任何缺点。
压缩将使你的 JS 代码体积减少 50% 到 90%!(如果你有很多评论的话 😀)

压缩
减轻 JS 负担最神奇的解决方案是二进制压缩。大多数情况下,你甚至不需要配置任何工具,你的 HTTP 服务器会使用 Gzip 或 Brotli 压缩你的文件。关于压缩的最佳实践,请参阅Antoine Caron的精彩文章 《为什么要使用 Webpack 压缩插件?》。
和其他解决方案一样,压缩效果很好,绝对值得使用。然而,这并不意味着你可以随意处理任意数量的 JS。
首先,无论压缩提供的减少百分比有多大,它仍然是原始重量的比例:10MB 的 30% 仍然是 3MB……

其次,压缩只是网络的一个产物。虽然网络传输的数据量减少了,但实际的代码量仍然相同。浏览器仍然需要解析和编译相同数量的代码。更糟糕的是,客户端必须先解压文件才能使用。这些步骤看似无关紧要,但在老款智能手机上,速度可能会非常慢,有时甚至比网络传输时间还要长!
以6play.fr为例:1MB 的 JS,在我的 MacBook 上的编译时间为 100ms,在 CPU 节流的情况下最多可达 500ms。

此图来自Addy Osmani及其文章《JavaScript 的成本》。他是这方面的重要参考,制定这类指标可能会很棘手。
请注意,大多数框架仅在二进制压缩后才会公布其大小。“Preact:React 的快速 3kB 替代方案”,但在压缩前实际大小为 8.7kB(顺便说一句,这仍然很棒)。
摇树
另一个减少代码量的神奇功能叫做 Tree Shaking。它是现代 JS 代码中静态分析导入代码的功能,以便自动检测未使用的部分并将其删除。
像 Angular 这样的框架在 Tree Shaking 方面投入了大量精力。他们优化了自己的源代码,只导入必要的代码。这样,他们确保只导入框架中必要的部分,并生成尽可能小的 bundle。
现在几乎所有捆绑器都默认提供 Tree Shaking。
服务器端渲染
首先,SSR 是 Web 框架在服务器端执行的能力,以便响应来自客户端的初始请求来提供完全计算的页面,从而允许用户在 JS 加载期间看到一些内容。
我是服务器端渲染的忠实粉丝,但今天我要指出它的局限性。
SSR 会缩短我们所说的首次渲染时间 (TTFP)。TTFP 指的是用户首次请求到用户实际看到内容之间的时间。对于内容网站来说,TTFP 尤为重要,对于 SEO 来说几乎是必需的(大多数爬虫不会执行 JS)。然而,当 TTFP 发生时,页面不会加载任何 JS,因此无法进行交互。
JS 加载完成后,框架会重新启动,对现有标记进行“水合”,然后才能处理用户事件。我们称之为“可交互时间”(TTI)。
在某些方面,SSR 甚至可能适得其反。主要是因为在服务器端运行框架会消耗时间和资源,而返回静态资源的速度更快。此外,大多数框架为了能够“水合”,需要导出服务器端使用的上下文,这也会造成一定的负担。
延迟加载
在单页应用 (SPA) 的早期,我们习惯于将所有 JS 代码打包在一起。这意味着,一旦我们请求一个 Web 应用,就得先发送应用所需的所有源代码,之后才能执行任何操作。这种做法很糟糕,幸运的是,框架和工具的发展使得管理 JS 代码的延迟加载变得越来越容易。
一个良好的延迟加载机制意味着你只会在启动应用程序时下载启动所需的代码。其余代码将在需要时再加载。
不过,如果您需要大量代码来运行您的应用程序,那么在某个时候,您就需要加载它。
编译与运行时
最近出现了一种新方法。通过编译部分应用程序代码,可以减少需要加载的库的大小。在编译器中,编译后的代码所使用的公共代码称为运行时。
有两个框架阐述了这一理念。Angular 从 4 开始就提出在构建时编译模板代码(该功能称为“提前编译”或 AOT),以节省多达一半的代码体积。第二个框架是 Svelte,它是第一个提出“完全没有运行时”理念的框架。
编译是一种有趣的策略,但要小心适得其反。编译后的代码最终会比源代码更大,而使用运行时可能会生成更小的包。
等等…
有很多工具和策略可以减轻 JS 的负担。我没有全部提到,还有缓存、CDN 等等,当然还有一些我遗漏了的。
但是您现在明白了,这些技术都不是绝对的,您仍然必须始终关心您发送的代码的重量。
事物的重量
为了管理您的应用程序依赖项并考虑它们的权重,您必须知道事物的权重。
有一些流行的插件,例如VS Code Import Cost或Webpack Bundle Analyzer,可以显示您在代码中执行的每个导入的权重。


它们看起来很漂亮,但要小心,因为问题在于,担心代码编辑器的重量已经太晚了。在选择堆栈时,你必须提前考虑这个问题。
如前所述,常用的测量方法有很多种,主要是压缩前后的测量。为了方便起见,我将从现在开始使用压缩前(但缩小后)的数据。
常用框架和库
无需过多评论,让我们看看目前最常用的流行库的权重(如果你的库不在那里,很抱歉)
- Lodash:72kB
- jQuery:86kB
- Backbone:42kB(包括 Underscore,但也需要 jQuery)
- AngularJS:172kB
- 反应:117kB
- Angular:使用 AoT 时为 299kB(Hello World 应用程序包),使用 JiT 时为 663kB
- Vue:91kB
- 预行动:8kB
- Ember:733kB(Hello World 应用程序包)
- 宣传单:138kB
- Bootstrap:152kB CSS,57kB JS
- Bulma:186kB CSS

你好世界重量与真实世界重量
当谈到 JS Web 框架时,必须进一步讨论数字。
现代框架都深度模块化。Angular 将每个模块视为框架的一部分,React 也将每个模块视为第三方。然而,你的应用经常需要这些模块,因此,只考虑核心框架的重量是错误的。
这会造成仅使用框架核心模块的 Hello World 应用与真实应用的重量差异显著。最近,Tree Shaking 的广泛使用加剧了这个问题。风险在于,某些框架会用一个非常简单的示例来传达一些奇怪的数字,而 Tree Shaking 会删除所有东西,而框架的实际大小并没有改变。

为了实现这些措施,我将应用程序与框架、状态管理框架、路由器和 Material Design 组件库捆绑在一起(并使用 import all 来防止 Tree Shaking)。它与“现实世界”的应用程序并不完全一样,而是非常相似。
我们看到,一个框架的初始权重并不一定代表最终结果。然而,Vue 的权重似乎主要归功于 Vuetify,因为它包含非常多的组件。
代码的重量
在我所知的任何其他平台上(硬件开发领域肯定也存在这种情况),代码大小其实并不重要。我记得有些 Java 应用程序的依赖包有几百兆,而我的 JAR 文件只有一两兆。这并非 Java 的恶作剧,在这种情况下,代码大小真的无关紧要。
在前端 JS 中,你不能这样想。你的依赖必须很小,而且你的代码很快就会比你的框架更大。
这不一定是错的。大型 Web 应用确实存在,它们也需要代码。但你应该意识到这是一个问题,并且应该使用之前解释过的所有技巧来尽量减少代码量。
例如,我曾经开发过一个大型 Web 应用程序,其中供应商大约为 350kb(压缩后),具体代码也大致相同。
其他事物的重量
到目前为止,我只讨论了 JS 文件的重量,但一个网站是由多种不同类型的内容组成的。在考虑 JS 文件的重量时,还必须考虑整体图片的重量。
您的网站肯定还包含 HTML、CSS、图像、字体、视频……
- 除了特殊情况外,HTML 会非常轻,但将其最小化总是好的。
- CSS 的大小可能从几百 KB 到超过 1MB 不等。所以必须考虑到这一点。死代码消除、延迟加载、代码压缩,所有这些技术都可以应用于 CSS。
- 图片通常是网站上加载量最大的部分。它们经常被用作逃避 JS 文件加载的借口,理由是“它只占整个页面的一小部分”。图片对页面加载量至关重要。你必须仔细优化图片,下载尽可能小的版本,延迟加载某些版本,有时甚至移除其他版本。如果处理得当,图片的加载量可以比 JS 文件还小。
- 字体是网页优化中经常被忽略的一种内容类型,但它们非常重,并且是页面上显示某些内容的渲染瓶颈。
- 视频是一种非常特殊的内容类型。它占用很大空间,但通常不会自动加载,压缩率也很高(除了 gif 动图😂),而且大多数情况下都是流式传输的,所以在这种情况下,它不会直接影响网站的加载时间。
根据HTTP Archive State of the Web 的数据,目前网站的平均大小在桌面端为 1.8MB,在移动端为 1.6MB。其中 JS 占比 22%,约为 400kB(在线上)。
结论
Web 是一个平台,无论你做什么,代码的权重都至关重要。我们拥有一个充满优秀工具的生态系统来优化 Web,但并没有灵丹妙药,迟早你都要为此付出代价。
它推动社区推进新框架、新功能的发展,而无需添加更多代码行,有时甚至更少(VueJS 比 AngularJS 小)。
它有一个非常酷的象征,其中每一行代码都是珍贵的,精心制作的,社区必须随着纯粹的新想法而发展,而不仅仅是将新代码堆积在旧代码上。

本文中的所有数据均来自此 GitHub 存储库:
鏂囩珷鏉ユ簮锛�https://dev.to/swiip/the-weight-of-js-1aad