调试webpack源码的两种方法
node —inspect-brk
node --inspect-brk node_modules/webpack-cli/bin/cli.js
yarn link
创建软连接,便于运行webpack开发环境的源码
# 切换webpack 和webpack-cli的版本
git reset --hard webpack-cli@4.2.0
$ git reset --hard v5.10.1
$ cd packages/webpack-cli/ 进入根目录
$ yarn link
success Registered "webpack-cli".
info You can now run `yarn link "webpack-cli"` in the projects where you want to use this package and it will be used instead.
Done in 0.36s.
这样后在任何地方运行 yarn link webpack-cli就都会创建一个文件软连接
这样实现了在一个项目中运行外部文件的webpack-cli
$ node ./node_modules/webpack-cli/bin/cli.js
webpack-cli是如何调用webpack的?
webpack-cli-bin/cli.js
webpack = require('webpack')
compiler = webpack(options, callback)
看cli的源码得知
- webpack-cli是通过调用webpack函数来创建(createCompiler函数)一个编译器
webpack开始
- Compiler类(./lib/Compiler.js):webpack的主要引擎,代表的是不变的webpack环境- 在compiler对象记录了完整的webpack环境信息,在webpack从启动到结束,compiler只会生成一次。
- 可以在compiler对象上读取到webpack config信息,outputPath等;
 
- 在compiler对象记录了完整的webpack环境信息,在webpack从启动到结束,
- Compilation类( - ./lib/Compilation.js):代表了一次单一的版本构建和生成资源。
- 我们知道打包器需要先分析并收集依赖,然后打包成一个文件 
- webpack是如何做这些处理的呢?
- 通过阅读lib/index.js源码- 其中发现了大量的hooks.xxx.call()代码
- 然后hooks函数是从tapable库中引入的
 
- 其中发现了大量的
- 那么tapable是什么?
Tapable这是一个监听和触发事件/钩子函数 的库,就是一个发布订阅系统
 用法- // 定义一个事件/钩子
- this.hooks.eventName = new SyncHook(["arg1", "arg2"]);
- // 监听一个事件/钩子
- this.hooks.eventName.tap('监听理由', fn)
- // 触发一个事件/钩子
- this.hooks.eventName.call('arg1', 'arg2')
 
webpack打包流程分析
调用了大量tapable 库中的hooks事件
- environment
- afterEnvironment
- initialize
- beforeRun
- run
- shouldEmit
- done
- beforeCompile
- afterCompile
index.js分析并收集是在哪个阶段?
- 不是env 和 emit,判断在beforeCompile与 afterCompile之间
- 最有可能在make - finishMake阶段
- 分析后发现EntryPlugin.js 这个插件文件- webpack就是一个事件模型,webpack本身并不做大量处理,而是将工作通过插件Plugin的形式分出去
 
factory函数
factory就是nmf
factory.create就是nmf.create的方法
factorizeModule
nmf.create方法做了什么
- 来到 NormalModuleFactory.js,可以看到 create 的代码
- 只发现一句有用的代码:beforeResolve.call和factorize.call
- 搜索两者对应的 tap,发现 factorize.tap 里面有重要代码 它触发了 resolve,而 resolve 主要是在收集 loaders
- 然后它触发了 createModule,得到了 createdModule
- 也就是说,nmf.create 得到了一个 module 对象
- 等价于 factory.create 得到了一个 module 对象
- 回到 factorizeModule,发现后续操作是 addModule 和 buildModule
addModule 做了什么
- 把module添加到compilation.modules属性里面
- 
buildModule 做了什么
- 它调用了 module.build() 
- 来到 NormalModule.js 看 build 源码-doBuild,发现了 runLoaders() ,运行所有合适的loader
- 然后来到 processResult(),发现了 _source = … 和 _ast = null 这是要做什么?
- 显然是要把 _source 变成 _ast 了!来到 doBuild 的回调,发现了 this.parser.parse()
- parse 就是把 code 变成 ast
- parser 是什么?继续跟代码会发现 parser 来自于 acorn 库,需要编译原理知识
webpack如何确定index依赖文件?
- webpack会对index.js进行parse得到ast抽象语法数
- 然后会在traverse过程中遍历这个ast,寻找import语句
阅读源码后发现
- blockPreWalkStatement() 对ImportDeclaration进行了检查
- 一旦发现 import ‘xxx’,就会触发 import 钩子,对应的监听函数会处理依赖
- 其中 walkStatements() 对 ImportExpression 进行了检查
- 一旦发现 import(‘xxx’),就会触发 importCall 钩子,对应的监听函数也会处理依赖
webpack如何将modules合并成为一个文件的?
- compilation.seal(),该函数会创建 chunks、 为每个 chunk 进行 codeGeneration
- 然后为每个 chunk 创建 asset seal()
- 之后,emitAssets()、emitFiles() 会创建文件(emit 触发)
- 最终得到 dist/main.js 和其他 chunk 文件
看源码时的笔记
environment
afterEnvironment
initialize
beforeRun
run
----this.readRecords---
----this.compile----
beforeCompile
compile
----------this.newCompilation-------
make
finishMake
-----nextTick----
-----compilation.finish-----
finishModules
-----compilation.seal()-----
seal
afterOptimizeDependencies
beforeChunks
------this.addChunk-----
------buildChunkGraph----
afterChunks
optimize
optimizeModules
optimizeChunks
afterOptimizeChunks
optimizeTree
afterOptimizeTree
optimizeChunkModules
------this.codeGeneration-----
------this.createChunkAssets---
processAssets
afterSeal
afterCompile
----onCompiled---
shouldEmit
----nextTick----
----this.emitAssets---
----this.emitRecords---
done
总结
- 调用webpack函数接收config配置信息,并初始化compiler,在此期间会apply所有 webpack 内置的插件;
- 调用compiler.run进入模块编译make阶段,- 用JavascriptParser实例对象对 index.js 进行 parse编译 得到 ast抽象语法数
- 然后遍历 ast 发现依赖声明就将其添加到 module 的 dependencies 或 blocks 中
- seal 阶段,webpack 将 module 转为 chunk,可能会把多个 module 通过 codeGeneration 合并为一个 chunk seal
 
- 用
- 最后,为每个 chunk 创建文件,并写到硬盘上
 
                         
                                

