通过代码拆分减少 JavaScript 载荷

大多数网页和应用均由许多不同的部分组成。而不是 系统会尽快发送构成应用的所有 JavaScript, 网页在加载后将 JavaScript 分成多个块 提高网页性能。

此 Codelab 介绍了如何使用代码拆分来提高 对三个数字进行排序的简单应用。

一个浏览器窗口显示了一个名为 Magic Sorter 的应用,该应用有三个用于输入数字的字段和一个排序按钮。

测量

与往常一样,必须先衡量网站的效果 尝试添加任何优化措施

  1. 如需预览网站,请按查看应用。然后按 全屏 全屏
  2. 按 `Ctrl+Shift+J`(在 Mac 上,按 `Command+Option+J`)打开开发者工具。
  3. 点击网络标签页。
  4. 选中停用缓存复选框。
  5. 重新加载应用。

显示 71.2 KB JavaScript 软件包的 Network 面板。

只是为了对一个简单应用中的几个数字进行排序,实际需要花费 71.2 KB 的 JavaScript 代码。 这是怎么回事?

在源代码 (src/index.js) 中,导入并使用 lodash 库 应用。Lodash 提供了许多实用的实用程序 函数,但这里只使用 包中的一个方法。 安装并导入整个第三方依赖项,其中只有一个 但有一部分被利用都是比较常见的错误。

优化

您可以通过以下几种方式来缩减 bundle 大小:

  1. 编写自定义排序方法,而不是导入第三方库
  2. 使用内置的 Array.prototype.sort() 方法按数字排序
  3. 仅从 lodash 导入 sortBy 方法,而非整个库
  4. 仅在用户点击按钮时下载用于排序的代码

方法 1 和方法 2 是缩减 bundle 大小(并且 可能最适合实际应用的情况)。但这些是 本教程中未用到的一些功能,只是供教 😈? 时使用。

选项 3 和选项 4 都有助于提高此应用的性能。通过 在此 Codelab 的后面几个部分中,我们将介绍这些步骤。就像编写任何代码 教程中,请始终尝试自行编写代码,而不是复制和粘贴。

仅导入您需要的内容

需要修改一些文件,以便仅从 lodash 导入单个方法。 首先,替换 package.json 中的以下依赖项:

"lodash": "^4.7.0",

替换为:

"lodash.sortby": "^4.7.0",

现在,在 src/index.js 中导入此特定模块:

import "./style.css";
import _ from "lodash";
import sortBy from "lodash.sortby";

然后更新值的排序方式:

form.addEventListener("submit", e => {
  e.preventDefault();
  const values = [input1.valueAsNumber, input2.valueAsNumber, input3.valueAsNumber];
  const sortedValues = _.sortBy(values);
  const sortedValues = sortBy(values);

  results.innerHTML = `
    <h2>
      ${sortedValues}
    </h2>
  `
});

重新加载应用,打开开发者工具,然后查看 Network 面板 。

显示 15.2 KB JavaScript 软件包的 Network 面板。

对于此应用,其软件包大小缩减了 4 倍多,且 但仍有改进的空间。

代码拆分

webpack 是最受欢迎的开源软件之一 当前使用的模块打包器简而言之,它捆绑了所有 JavaScript 模块(作为 以及将组成 Web 应用的静态文件 供浏览器读取

此应用中使用的单个软件包可拆分为两个单独的软件包 数据块:

  • 负责组成初始路由的代码
  • 包含排序代码的次要数据块

通过使用动态导入,可以延迟加载辅助分块,或者 按需加载。在此应用中,构成数据块的代码可以是 仅在用户按下该按钮时加载。

首先,移除 src/index.js 中排序方法的顶级导入项:

import sortBy from "lodash.sortby";

然后将其导入到按下按钮时触发的事件监听器中:

form.addEventListener("submit", e => {
  e.preventDefault();
  import('lodash.sortby')
    .then(module => module.default)
    .then(sortInput())
    .catch(err => { alert(err) });
});

import() 功能是 提案(目前处于阶段 TC39 流程中的 3 部分)添加动态导入模块的功能。 webpack 已包含对此项的支持,并遵循 被提案取消

import() 返回一个 promise,在解析后,选定的 模块中将该模块拆分为一个单独的块。完成本单元的学习后 module.default 用于引用默认的 即 lodash 提供的导出内容。promise 与另一个 .then 链接, 调用 sortInput 方法来对三个输入值进行排序。在 promise 链。catch() 用于处理 promise 被拒绝的情况 发生错误。

需要完成的最后一项任务是在sortInput 。该函数必须是返回 接受从 lodash.sortBy 导入的方法。然后,嵌套函数 对三个输入值进行排序并更新 DOM。

const sortInput = () => {
  return (sortBy) => {
    const values = [
      input1.valueAsNumber,
      input2.valueAsNumber,
      input3.valueAsNumber
    ];
    const sortedValues = sortBy(values);

    results.innerHTML = `
      <h2>
        ${sortedValues}
      </h2>
    `
  };
}

监控

最后一次重新加载应用,并密切关注网络 面板。应用一经运行,便只会下载一个小的初始 bundle 。

显示 2.7 KB JavaScript 软件包的 Network 面板。

按下按钮对输入数字进行排序后,包含 系统会提取并执行排序代码

Network 面板,显示 2.7 KB JavaScript 软件包,后跟 13.9 KB JavaScript 软件包。

请注意数据是如何排序的!

总结

代码拆分和延迟加载是精简代码 应用的初始 bundle 大小,这直接导致 网页加载速度会快得多不过,您需要注意一些重要事项 在向应用添加此优化之前要考虑的因素。

延迟加载界面

延迟加载特定代码模块时,请务必考虑 将改善用户体验拆分和 在用户提交操作时加载一大段代码会使 该应用似乎已停止运行,因此请考虑显示 某种加载指示器

延迟加载第三方节点模块

在 这取决于您的使用位置。通常,第三方 依赖项会拆分为单独的 vendor bundle,该 bundle 可以进行缓存, 它们的更新频率较低。详细了解 SplitChunksPlugin 可以 可以帮助您实现这一目标。

使用 JavaScript 框架实现延迟加载

许多使用 Webpack 的常用框架和库都为 相较于在开发应用时使用动态导入 应用。

虽然了解动态导入的工作原理很有帮助,但始终使用 方法(由框架/库推荐的方法来延迟加载特定模块)。

预加载和预提取

请尽可能利用浏览器提示,例如 <link rel="preload"><link rel="prefetch">,以尝试加载关键模块,甚至 加快实施速度。webpack 通过在导入中使用魔术注释来支持这两种提示 语句。有关详情,请参阅 预加载关键分块指南。

延迟加载不仅仅是代码

图像可以构成应用的重要组成部分。延迟加载 非首屏或设备视口外),都可以加快网站的加载速度。已读 请参阅 Lazysizes 指南。