高级前端面试中经常有这么几道题:
- 说一说 loader 和 plugin 的区别
- webpack 构建流程是怎样的
- 编写 webpack loader 的思路
- 编写 webpack plugin 的思路
网上能搜到一些答案,但是这些答案我一一看过了,要么过于肤浅留于表面,要么冗长繁杂难以卒读。
因此,我花了一个星期把 Webpack 5 的源码逐行扫了一遍,理出了主要脉络。整个阅读过程我录制成了视频,,总时长不到 3 小时,但把 Webpack 的整体架构、打包思路、loader 实现思路、plugin 实现思路、parser 运行流程等都讲到了,最重要的是,通过视频你会掌握「阅读源码的技巧」。
如果你需要此视频课程,请加微信 xiedaimala03
接下来是文字教程(文字教程并不适合用来阐述源码,但好在免费):
webpack-cli 是如何调用 wepack 的
webpack = require('webpack')
compiler = webpack(options, callback)
hooks.xxx.call 是什么
Tapable 是 webpack 团队为了写 Webpack 而写的一个事件/钩子库
用法
定义一个事件/钩子
this.hooks.eventName = new SyncHook(["arg1", "arg2"]);
监听一个事件/钩子
this.hooks.eventName.tap('监听理由', fn)
触发一个事件/钩子
this.hooks.eventName.call('arg1', 'arg2')
Webpack 的整体流程是怎样的
至少有 env init run beforeCompile compile compilation make finishMake seal optimize afterCompile emit 等钩子
读取 index.js 并分析和收集依赖是在哪个阶段?
make - finishMake 阶段
make - finishMake 之间,做了什么
- 搜索 make.tap,发现很多地方监听了 make 事件
- 凭借我们的直觉,我们直接打开 EntryPlugin
- EntryPlugin 的 addEntry 函数就是 make 阶段最重要的事情之一
factory.create 中的 factory 是什么东西?
这个 factory 是哪里来的?
是从 factorizeModule(options 的 options.factory 来的。
这个 options.factory 是哪里来的?
是从 moduleFactory 来的。
moduleFactory 哪里来的?
是用 this.dependencyFactories.get(Dep) 得到的。
this.dependencyFactories.get(Dep) 是个啥?
你搜 compilation.tap 就知道,它是 normalModuleFactory,简称 nmf
老师,你 TM 怎么知道要搜这个?
我把所有钩子都搜了,搜了半个小时,能不知道吗?
结论:factory 就是 nmf,所以 factory.create 就是 nmf.craete
nmf.create 做了什么?
- nmf.create 得到了一个 module 对象
- 后续操作是 addModule 和 buildModule
addModule 做了什么?
- 跟前面课程的思路类似,把 module 添加到 compilation.modules 里
- 而且还通过检查 id 防止重复添加
buildModule 做了什么?
- 看名字就知道是重要操作,它调用了 module.build()
- 来到 NormalModule.js 看 build 源码,发现了 runLoaders
- 然后来到 processResult(),发现了 _source = ... 和 _ast = null
- 这是要做什么?显然是要把 _source 变成 _ast 了!
- 来到 doBuild 的回调,发现了 this.parser.parse() !
- parse 就是把 code 变成 ast
- 问题来了,parser 是什么,parse() 的源码在哪?
- 继续跟代码会发现 parser 来自于 acorn 库,需要编译原理知识,不跟进了
- 如果你想要学习编译原理知识,可以购买我的科班课程
Webpack 如何知道 index.js 依赖了哪些文件
- blockPreWalkStatement() 对 ImportDeclaration 进行了检查
- 一旦发现 import 'xxx',就会触发钩子,对应的监听函数会处理依赖
- 其中 walkStatements() 对 ImportExpression 进行了检查
- 一旦发现 import('xxx'),就会触发钩子,对应的监听函数也会处理依赖
- 这里不讨论 require,大家有兴趣可以自己研究
Webpack 是怎么把 modules 合并成一个文件的?
- 看 compilation.seal(),该函数会创建 chunks、为每个 chunk 进行 codeGeneration,然后为每个 chunk 创建 asset
- seal() 之后,emitAssets()、emitFiles() 会创建文件(emit 就是射)
* 最终得到 dist/main.js 和其他 chunk 文件
今年,我将研读更多前端项目源码并做成新的视频课程,敬请期待。
如果有什么开源项目的源码是你想了解的,欢迎留言。
完。