Skip to content

xianjianlf2/mini-webpack

Repository files navigation

mini-webpack

这个仓库会通过 Git 提交历史,一步步把 mini-webpack 搭出来。

目标不是尽快把功能堆满,而是按最适合理解 webpack 的顺序,让同一个核心打包流程慢慢长出来。

换句话说,这个仓库更在意“为什么下一步应该是它”,而不是“还能不能再多加一个功能”。

怎么看这条历史

每一条提交只做一件事,所以这条历史本身就是学习路线。

你可以先看完整代码,再按顺序回看提交,观察同一个 index.js 是怎么一步步长出来的。

常用方式:

git log --oneline --reverse

如果你想看某一步的具体变化,可以继续用:

git show <commit-id>

如果你想按顺序体验每一步,可以从最早的提交开始,一步步切过去看。

为什么要按这个顺序

如果目标是学 webpack 的核心,而不是尽快做一个能处理所有文件的打包器,那顺序非常重要。

最容易走偏的方式,是太早去做这些东西:

  • 先做 loader 大全
  • 先做 plugin 大全
  • 先做复杂配置
  • 先做压缩、分包、热更新

这样虽然看起来“功能更多”,但会把 webpack 最核心的那条主线打散。

webpack 真正最值得先弄明白的,是这条链:

  1. 从哪个文件开始
  2. 怎么发现它引用了谁
  3. 怎么继续向下找到更多文件
  4. 怎么给每个文件一个稳定编号
  5. 怎么把多个文件放进一个最终文件
  6. 怎么让最终文件自己能运行
  7. 怎么处理不是普通代码的文件
  8. 怎么把转换和流程扩展交给外部

只有这条主线通了,后面的 loader、plugin、缓存、分包,才知道该挂在哪、为什么能挂上去。

推荐学习顺序

步骤 当前问题 新增能力 为什么重要
1 打包从哪里开始 入口文件和输出文件 先跑通最小闭环
2 入口引用了别的文件怎么办 找到第一个依赖并合并 从单文件走向多文件
3 依赖还能继续引用依赖怎么办 递归查找依赖 得到完整文件关系
4 同一个文件被多次引用怎么办 复用同一个文件和运行结果 避免重复打包和重复执行
5 JSON 不是代码怎么办 先把 JSON 转成代码 引出文件转换
6 转换规则写死怎么办 外部配置 loader 让核心保持通用
7 loader 需要和打包器沟通怎么办 loader 上下文 让转换过程能传回信息
8 想改打包流程怎么办 plugin 钩子 让外部参与流程
9 CSS 不是代码怎么办 CSS loader 让样式也能被入口引用

整体流程

flowchart LR
  A["入口文件"] --> B["读取内容"]
  B --> C["按规则转换"]
  C --> D["分析引用"]
  D --> E["建立文件关系"]
  E --> F["生成最终文件"]
  F --> G["运行最终文件"]
Loading

第一步

先只做一件最小的事:找到入口文件,把它放进最终文件里,并确认最终文件能运行。

这一步故意不处理引用关系,因为现在最重要的问题还不是“有多少文件”,而是“打包工具到底从哪里开始”。

为什么下一步不是直接做 loader 或 plugin?

反例是:如果入口和输出都没跑通,就急着做扩展能力,后面任何问题都分不清是核心流程错了,还是扩展机制错了。

第二步

让入口文件引用第一个普通代码文件。

这一版开始解决一个真实问题:入口文件通常不会把所有代码都写在自己里面,它会引用别的文件。

这一步主要做三件事:

  1. 读入口文件
  2. 找到入口文件引用了谁
  3. 把入口和被引用文件一起放进最终文件

为什么下一步不是立刻做 JSON 或 CSS?

反例是:如果连普通代码文件之间怎么互相找到都没弄明白,直接处理更多文件类型,只会把问题混在一起。

第三步

让被引用的文件也能继续引用别的文件。

这一版解决的是“只看入口一层不够”的问题。

入口文件引用 foo.jsfoo.js 又引用 message.js。打包工具不能只停在入口,而是要顺着这条线继续往下找。

为什么要从当前文件的位置继续找?

反例是:如果永远从入口目录找文件,一旦文件放进子目录,里面的相对引用就会找错地方。

第四步

让同一个文件只进入最终文件一次,并且运行时只加载一次。

这一版解决的是“重复引用”的问题。

入口文件直接引用了 message.jsfoo.js 也引用了同一个 message.js。打包工具应该知道这其实是同一个文件,而不是复制两份。

为什么不是每次用到都重新执行?

反例是:如果一个文件里有初始化逻辑,每次引用都重新执行,就可能重复初始化,结果会不稳定。

第五步

让 JSON 文件也能被入口使用。

这一版解决的是“不是所有被引用的文件都是普通代码”的问题。

JSON 不能直接当作代码执行,所以要先把它变成代码能理解的样子,再继续参与打包。

为什么不是把 JSON 单独扔出去?

反例是:如果代码里写的是 import user from './user.json',但打包工具只是把 JSON 另存一份,最终代码还是不知道 user 从哪里来。

第六步

把文件转换规则交给外部配置。

这一版解决的是“转换规则不应该写死在核心里”的问题。

核心打包流程只负责读配置、匹配规则、执行转换。至于 JSON 怎么转,交给外面的转换函数。

为什么转换要发生在找依赖之前?

反例是:JSON 原始内容不是代码,如果先找依赖,解析阶段就已经失败了。要先把它变成代码能理解的样子,后面才能继续分析。

第七步

让转换函数也能把信息交回给打包器。

这一版解决的是“转换函数不一定只是改内容”的问题。

比如处理某个文件时,转换函数可能想记录自己处理过谁。打包器给它一个可以沟通的入口,它就不再只是一个孤立函数。

为什么不是只让转换函数返回字符串?

反例是:如果以后转换过程还要记录额外文件、缓存信息或调试信息,只返回字符串就不够用了。

第八步

给打包过程加插件入口。

这一版解决的是“不是所有扩展都适合放在文件转换阶段”的问题。

loader 更适合处理文件内容;plugin 更适合参与打包流程,比如在写出最终文件前改变输出位置。

为什么不继续用 loader 做所有事?

反例是:改变输出文件名并不是某个源文件的内容转换,把它塞进 loader 会让职责混乱。

第九步

让 CSS 文件也能被入口使用。

这一版解决的是“样式文件不是普通代码”的问题。

CSS 原始内容不能直接在最终文件里执行,所以 loader 会把 CSS 文本包成一段普通代码。最终文件在浏览器里运行时,可以把样式放进页面;在测试里,也能把这段 CSS 当作字符串拿来确认。

为什么 CSS loader 不应该改核心打包流程?

反例是:如果每来一种文件类型都去改 index.js,核心流程会越来越乱。更好的方式是让核心只认“外部规则”,具体怎么转换交给对应 loader。

最终能做什么

现在这个 mini-webpack 可以:

  • 从入口文件开始打包
  • 自动找到普通代码依赖
  • 继续向下找到嵌套依赖
  • 避免重复打包同一个文件
  • 避免重复执行同一个文件
  • 通过 loader 处理 JSON
  • 通过 loader 处理 CSS
  • 通过 plugin 改变输出位置

怎么验证

运行:

npm test

它会先生成最终文件,再运行最终文件,确认输出符合预期。

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors