通过代码拆分减少 JavaScript 载荷

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

发送大型 JavaScript 载荷会显著影响网站的速度。不要在加载应用的第一页后立即向用户提供所有 JavaScript,而应将软件包拆分为多个部分,仅在最开始发送需要的内容。

为什么代码拆分有益?

代码拆分是一种旨在最大限度地缩短启动时间的技术。如果我们在启动时加载的 JavaScript 更少,那么我们可以通过最大限度地减少此关键阶段的主线程工作,使应用更快地进行交互

对于核心网页指标,减少启动时下载的 JavaScript 载荷将有助于提高 First Input Delay (FID)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
}

构成模块的代码不会添加到初始 bundle 中,而是延迟加载,或者仅在表单提交后才需要时提供给用户。为了进一步提高网页性能,请预加载关键区块以加快优先级并更快地提取它们

虽然前面的代码段只是一个简单的示例,但延迟加载第三方依赖项在大型应用中不是常见模式。通常,第三方依赖项会拆分为单独的供应商软件包,这些软件包可缓存,因为它们的更新频率不高。您可以详细了解 SplitChunksPlugin 如何帮助您做到这一点。

使用客户端框架时,在路由或组件级别拆分是延迟加载应用不同部分的更简单的方法。许多使用 webpack 的热门框架都提供抽象化,使延迟加载比自行深入配置更容易。