minipack 是 Ronen Amiel 的迷你打包器实现,用几百行代码还原 webpack 的核心打包流程。它不包含 webpack 的工程化外壳(dev server、HMR、代码分割等),只保留"入口 → 依赖图 → 输出 bundle"的主干,是理解模块打包的第一手材料。
基础打包流程
- 读取入口文件:接收文件名并读取内容。
- 生成 AST:使用 babylon 将源码解析为 AST(abstract syntax tree),使代码可以被程序化分析。
- 收集依赖:遍历 AST 找到
import/require语句,获取依赖文件列表;对每个文件生成 asset 对象,记录它的 id、code 和 dependencies,构建出整张依赖图。 - 转译代码:根据 code 使用 preset-env 处理并转义为新的兼容代码,把
import等 ESM 语法转换成 CommonJS 形式,并将依赖路径替换为 asset id。 - 拼接输出:把所有 asset 的转译后代码拼接成新的 JS 代码,通过一个自定义模块加载函数(
require+ 模块缓存)组织成一个可执行的 bundle。
核心洞察:打包的本质是把"文件间的引用关系"重写为"运行时模块注册表"。所有模块的依赖在编译期收集完毕,运行时只需要按 id 查找和调用。
带 loader 的版本
在基础流程上接入 loader 后,第 4 步之前多了一层资源转换:
- 根据 filename 获取匹配到的 loader(webpack 通过
module.rules的 test 正则匹配文件扩展名)。 - babel-loader 排在 loader 列表最前面,保证源码先被 babel 转译(语法降级),再进入后续 loader。
- 使用 loader-runner 包执行匹配到的 loaders,loader 的输出(转换后的 code)继续参与依赖收集与最终拼接。
loader 的本质是"对匹配文件的转换函数":输入文件内容(或上一个 loader 的输出),输出新的内容。这让 webpack 可以对任意资源(JS、CSS、图片、字体)统一做"解析 → 转换 → 打包"。
与 webpack 的对照
| minipack | webpack |
|---|---|
| babylon | acorn/parser 等 |
| asset 对象 | Module + Chunk |
| 自定义 require | webpack runtime(__webpack_require__) |
| preset-env 转译 | babel-loader 等 |
| loader-runner 执行 loader | 同一 loader-runner(webpack 内部复用) |
理解了 minipack,再看 webpack 官方文档的打包流程描述(entry → module resolution → loaders → plugin hooks → output)会清晰很多:webpack 只是在这个骨架上加了性能、缓存、代码分割和庞大的插件生态。
Discussion
留言与讨论
想法、补充和不同意见都欢迎。