Published on

性能优化篇(一) : 分包

翻译至: Bundle Splitting

此系列文章:

性能优化篇(二) : 压缩Javascript

性能优化篇(三) : 动态导入

性能优化篇(四) : Prefetch

性能优化篇(五) : Preload

性能优化篇(六) : PRPL模式

性能优化篇(七) : Tree Shaking

Bundle Splitting

在构建现代 Web 应用程序中,WebpackRollup 等打包工具会将应用程序的源代码,并将其打包到一个或多个模块包中。当用户访问网站时,会请求并加载这些模块包,将数据显示到用户的屏幕上。

V8 等 JavaScript引擎能够解析和编译用户在加载时请求的数据。尽管现代浏览器已经发展到尽可能快速和高性能地解析和编译代码,但开发人员仍然需要优化该过程中的两个步骤:请求数据的加载时间和执行时间。我们希望确保执行时间尽可能短,以防止阻塞主线程。

尽管现代浏览器能够在数据包到达时对其进行 stream 传输,但在第一个像素绘制到用户设备上之前仍然需要很长时间。包越大,引擎执行到UI对应的代码所需的时间就越长。在那之前,用户必须盯着空白屏幕相当长一段时间,这可能会......非常令人沮丧。

我们希望尽快向用户显示数据。较大的包会导致加载时间、处理时间和执行时间增加。如果我们能够缩小包的大小以提升加载速度,那就太好了。

我们可以将包拆分为多个较小的包,而不是请求一个包含很多不必要代码的巨大的模块包。

通过打包工具分割,可以减少加载、编译和执行模块包所需的时间。减少加载和执行时间,我们可以缩短在用户屏幕上绘制第一个内容(FCP - First Contentful Paint)之前所花费的时间,以及将最大组件渲染到屏幕上之前所花费的时间(LCP - Largest Contentful Paint)。

虽然能够在屏幕上看到UI很棒,但我们并不仅仅是想看到静态内容。为了拥有一个功能齐全的应用程序,我们希望用户也能够与其交互。而这只有在所有Bundle加载并执行后,UI 才会具有交互性。所有内容绘制到屏幕上并进行交互所需的时间被称为交互时间。

更大的包并不一定意味着更长的执行时间。我们可能会加载大量用户压根不会使用的代码。也许某部分的功能只有某些用户使用,甚至,用户可能都永远不会用到那些代码对应的功能!

在用户能够在屏幕上看到任何内容之前,JS引擎必须加载、解析和编译在首次渲染中未使用的代码,尽管浏览器可以以高性能的方式处理它们,使得解析和编译成本实际上可以忽略不计。但加载执行这些包仍然可能降低应用程序的性能。使用低端设备或较慢网络的用户会发现在获取包之前加载时间显着增加。

即使引擎只使用源码的最后部分,但第一部分仍然必须加载和编译。我们可以将初始页面中低优先级的代码与用于渲染初始页面所需的代码分开,而不是请求那些低优先级的代码。

通过将大包分成两个较小的包 main.bundle.jsemoji-picker.bundle.js ,我们通过获取更少的数据来减少初始加载时间。