通过代码拆分减少 JavaScript 载荷

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

发送大型 JavaScript 载荷会严重影响网站速度。不要在应用的第一页加载完毕后立即将所有 JavaScript 发送给用户,而是将软件包拆分为多个部分,并在最开始时仅发送必要的部分。

代码拆分有何好处?

代码拆分是一种旨在最大限度缩短启动时间的技术。如果在启动时减少 JavaScript 的传送量,我们就可以最大限度地减少此关键期间的主线程工作,从而使应用更快地实现互动

对于 Core Web Vitals,减少启动时下载的 JavaScript 载荷有助于缩短 Interaction to Next Paint (INP) 时间。这样做的原因是,通过释放主线程,应用能够减少与 JavaScript 解析、编译和执行相关的启动费用,从而更快地响应用户输入。

根据网站的架构(尤其是当网站严重依赖客户端渲染时),减小负责渲染标记的 JavaScript 载荷的大小可能会缩短 Largest Contentful Paint (LCP) 时间。如果浏览器在客户端标记完成之前延迟发现 LCP 资源,或者主线程太忙而无法呈现该 LCP 元素,则可能会发生这种情况。这两种情况都会延迟网页的 LCP 时间。

测量

如果执行网页上的所有 JavaScript 代码需要花费大量时间,Lighthouse 会显示失败的审核。

失败的 Lighthouse 审核,显示脚本执行时间过长。

拆分 JavaScript 软件包,以便在用户加载应用时仅发送初始路由所需的代码。这样可以最大限度地减少需要解析和编译的脚本量,从而缩短网页加载时间。

借助 webpackParcelRollup 等热门模块打包器,您可以使用动态导入来拆分软件包。例如,请看以下代码段,其中展示了一个在提交表单时触发的 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
}

构成模块的代码不会包含在初始软件包中,而是现在延迟加载,或者仅在用户提交表单后需要时才提供给用户。为了进一步提升网页性能,您可以预加载关键 chunk 以优先提取这些 chunk

虽然上面的代码段是一个简单的示例,但在大型应用中,延迟加载第三方依赖项并不是一种常见的模式。通常,第三方依赖项会拆分为单独的供应商软件包,由于它们不会经常更新,因此可以缓存。您可以详细了解 SplitChunksPlugin 如何帮助您实现此目的。

使用客户端框架时,在路由或组件级别进行拆分是一种更简单的方法,可用于延迟加载应用的不同部分。许多使用 webpack 的热门框架都提供抽象层,让延迟加载比您自己深入研究配置更简单。