通过代码拆分减少 JavaScript 有效负载

没人喜欢等待。如果网站加载时间超过 3 秒,超过 50% 的用户会放弃访问

发送大型 JavaScript 有效负载会严重影响网站的速度。与其在应用程序的第一个页面加载时就将所有 JavaScript 发送给用户,不如将您的捆绑包拆分为多个部分,并在最开始时仅发送必要的部分。

代码拆分为何有益?

代码拆分是一种旨在最大限度缩短启动时间的技术。当我们在启动时发送更少的 JavaScript 时,我们可以通过最大限度地减少此关键期间的主线程工作来使应用程序更快地具备互动性。

在核心 Web 指标方面,减少启动时下载的 JavaScript 有效负载将有助于缩短“交互到下次绘制”(INP) 时间。其背后的原因是,通过释放主线程,应用程序能够通过减少与 JavaScript 解析、编译和执行相关的启动成本,更快地响应用户输入。

根据您网站的架构(特别是如果您的网站严重依赖客户端渲染),减小负责渲染标记的 JavaScript 有效负载的大小可能会缩短最大内容渲染时间 (LCP)。当 LCP 资源在客户端标记完成后才被浏览器发现时,或者当主线程太忙而无法渲染该 LCP 元素时,可能会发生这种情况。这两种情况都可能延迟页面的 LCP 时间。

衡量

当执行页面上的所有 JavaScript 花费大量时间时,Lighthouse 会显示审核失败。

A failing Lighthouse audit showing scripts taking too long to execute.

拆分 JavaScript 捆绑包,以便在用户加载应用程序时仅发送初始路由所需的代码。这最大限度地减少了需要解析和编译的脚本量,从而加快了页面加载速度。

流行的模块捆绑器(如 webpack、Parcel 和 Rollup)允许您使用动态导入来拆分捆绑包。例如,考虑以下代码段,其中显示了一个 someFunction 方法的示例,该方法在提交表单时触发。

import moduleA from "library";

form.addEventListener("submit", e => {
  e.preventDefault();
  someFunction();
});

const someFunction = () => {
  // uses moduleA
}

在这里,someFunction 使用从特定库导入的模块。如果此模块在其他地方未使用,则可以修改代码块以使用动态导入,以便仅在用户提交表单时才获取它。

form.addEventListener("submit", e => {
  e.preventDefault();
  import('library.moduleA')
    .then(module => module.default) // using the default export
    .then(() => someFunction())
    .catch(handleError());
});

const someFunction = () => {
    // uses moduleA
}

构成模块的代码不会包含在初始捆绑包中,现在是延迟加载,或者仅在表单提交后需要时才提供给用户。为了进一步提高页面性能,预加载关键代码块以优先处理并更快地获取它们

虽然之前的代码段是一个简单的示例,但在较大的应用程序中,延迟加载第三方依赖项并不是一种常见的模式。通常,第三方依赖项被拆分为单独的供应商捆绑包,可以缓存它们,因为它们不会经常更新。您可以阅读更多关于 SplitChunksPlugin 如何帮助您实现此目的的信息。

当使用客户端框架时,在路由或组件级别进行拆分是延迟加载应用程序不同部分的更简单方法。许多使用 webpack 的流行框架都提供了抽象,使延迟加载比深入研究配置本身更容易。