•
阅读时长 2 分钟

webpack之详解 module、chunk、bundle

目录

module、chunk、bundle 经常都被叫成“包”,但排查构建时,它们指向的层次不同。一个依赖进了哪个 chunk,和最终下载了哪个文件,也不是完全相同的问题。

用一次动态 import,把源码和输出对起来看会直观一些。

在空目录运行 npm init -y 和 npm install —save-dev —save-exact webpack@5.99.9。下面用 Node API 构建,不依赖全局 webpack 命令。

创建 math.js:

export const used = (a, b) => a + b;

创建 lazy.js:

export default "lazy loaded";

创建 entry.js:

import { used } from "./math.js";
globalThis.bundleResult = used(2, 3);
import("./lazy.js").then((module) => {
  globalThis.lazyResult = module.default;
});

创建 build.cjs:

const webpack = require("webpack");
const path = require("path");
webpack(
  {
    mode: "production",
    entry: path.resolve("entry.js"),
    output: {
      path: path.resolve("dist"),
      filename: "main.js",
      chunkFilename: "[id].js",
    },
    devtool: false,
  },
  (error, stats) => {
    if (error || stats.hasErrors()) {
      console.error(error || stats.toString("errors-only"));
      process.exitCode = 1;
      return;
    }
    console.log(
      JSON.stringify(
        stats.toJson({
          all: false,
          assets: true,
          chunks: true,
          modules: true,
          usedExports: true,
        }),
        null,
        2,
      ),
    );
  },
);

node build.cjs 会输出 stats。dist 中有 main.js 和另一个异步 JS 文件,在这份配置里,动态模块形成了额外的 chunk。

module 是构建处理的单元

一般我们将一个源文件,认为是一个module。

入口、math.js 和 lazy.js 参与模块图。Loader 处理后的结果、生成的虚拟模块也可能成为 module,所以不能把 module 永远理解成一个物理源文件。

入口静态引用 math.js,动态引用 lazy.js。构建工具基于这些依赖决定如何组织加载。

chunk 组织模块,asset 是输出资源

入口对应初始加载部分,动态 import 对应后续加载部分。stats 中的 chunks 可以看到是否 initial,以及关联的 files。Webpack 的 runtime 还负责在浏览器里加载和执行异步部分。

asset 是最终输出的资源,包括 JS、CSS、图片和 source map。一个 chunk 可能关联多个文件,不是所有 asset 都能直接叫作应用 JS bundle。bundle 更多是讨论打包后代码文件时使用的说法。

如果开启模块拼接,stats 的 modules 还可能嵌套。只数顶层 modules,不一定得到全部源码模块的数量。

文件拆出来以后,怎么检查加载

创建 index.html,放在项目根目录:

<!doctype html><meta charset="utf-8" /><title>动态加载</title>
<script src="./dist/main.js"></script>

用编辑器的本地 HTTP 服务打开,观察 Network。这个入口立刻调用 import,所以异步文件也会立即开始请求;“拆成异步 chunk”并不等于“等用户点击才下载”。在控制台看 bundleResult 为 5,lazyResult 为 lazy loaded。

真正按交互延后加载,需要把 import 放到对应的事件或路由边界。是否提取 runtime、共享 chunk 和缓存策略会进一步改变产物,还是要看当前配置的 stats 和请求。规则参考 Webpack 代码分割文档。