前言
上篇文章是webpack执行逻辑即Compiler对象的生成,本文紧接着webpack函数之后的逻辑,主要是Compiler对象的run方法的执行逻辑。
Run方法执行前
在执行run方法执行前,webpack函数执行前还有一些其他重要的逻辑,也是之后会一一说明的这里暂不展开说明。
progress参数支持
该参数是用于打印编译的进度,实际上是创建/lib/ProgressPlugin插件对象,并调用compiler.apply方法来执行ProgressPlugin的apply方法。
watch相关
监听相关配置,如果需要监听文件则需要配置watch相关参数等,最主要的是开启watch后最后会调用Compiler对象的watch方法。
Run方法执行逻辑
调用Compiler的实例方法run,开启整个的编译过程。下面是run实例方法的主要执行逻辑图:
从上面逻辑可以看出整个执行顺序,这里需要注意的是:
run事件执行逻辑包含在before-run的回调函数中
this.applyPluginAsync('before-run', this, err => {
// 其它逻辑
this.applyPluginAsync('run', this, err => {
// 其它逻辑
this.compile(onCompiled)
})
})
这里需要注意的是applyPluginAsync,实际上该函数是Tapable类中的实例方法,主要的功能按照队列形式批量调用指定的事件。
compile实例方法执行
run事件对应的回调函数中主要是执行compile实例方法,主要的逻辑执行如下:
首先,newCompilationParams方法执行,该方法是用于生成Compilation对象需要的相关参数,具体是:
-
normalModuleFactory
-
contextModuleFactory
-
compilationDependencies
webpack能够解析三种文件路径:绝对路径、相对路径、模块路径,文件路径的解析依赖于resolver。webpack中有三种内置类型的resolver,其中两种如下: -
Normal:通过绝对路径或相对路径,解析一个模块
-
Context:通过给定的 context 解析一个模块
这里的normalModuleFactory和contextModuleFactory就是对应相应类型的resolver解析处理的模块工厂,例如normalModuleFactory是创建normalModule的。
其次,before-compile事件执行,这里主要的逻辑点是:
- newCompilation:创建一个Compilation对象
- make事件的执行
Compilation对象创建
Compilation构造函数中初始化相关的实例属性,这里需要主要关注1个属性的初始化过程:
- mainTemplate
注册一系列的事件:startup、render、local-vars、require、module-obj、require-extensions
实际上该属性对应的mainTemplate对象是定义__webpack_require__相关的最后输出文件的代码
主要注意的是mainTemplate类继承自Template,而Template继承了Tapable。
this-compilation事件
执行this-compilation事件,依据本系列的简单实例的执行逻辑,之前注册了this-compilation只有webpack函数执行过程中的JsonpTemplatePlugin注册了,具体逻辑如下:
compiler.plugin("this-compilation", (compilation) => {
compilation.mainTemplate.apply(
new JsonpMainTemplatePlugin()
);
compilation.chunkTemplate.apply(
new JsonpChunkTemplatePlugin()
);
compilation.hotUpdateChunkTemplate.apply(
new JsonpHotUpdateChunkTemplatePlugin()
);
});
mainTemplate等继承了Tapable了,所以mainTemplate.apply就是调用Tapable中的apply实例方法,即调用对应插件的apply方法。这里依次调用了JsonpMainTemplatePlugin、JsonpChunkTemplatePlugin、JsonpHotUpdateChunkTemplatePlugin对应的apply方法,实际上这些插件的apply逻辑也是注册相关的事件,具体有:
- local-vars
- jsonp-script
- require-ensure
- require-extensions
- bootstrap
- hot-bootstrap
- hash
- render
compilation事件
compilation事件的处理函数比较多,主要的内容是注册相应的事件以及dependencyFactories、dependencyTemplates添加相应值。其中事件具有有:
- render
- package
- hash
- parser
- normal-module-loader
- after-resolve
- alternatives
- optimize-chunks-basic
- optimize-extracted-chunks-basic
- finish-modules
- asset-path
- global-hash
- hash-for-chunk
- record-modules
- revive-modules
- record-chunks
- revive-chunks
- seal
applyPluginsParallel(‘make’, callback)
执行make事件,会找到在webpack函数执行过程中注册的make:
compiler.plugin("make", (compilation, callback) => {
const dep = SingleEntryPlugin.createDependency(this.entry, this.name);
compilation.addEntry(this.context, dep, this.name, callback);
})
调用Compilation的addEntry实例方法,由此从入口开始解析模块、收集依赖等操作。
主要的逻辑流程如下:
Compilation对象addEntry -> _addModuleChain -> 找到对应normalModule类型的moduleFactory -> 调用moduleFactory的create方法
这里需要注意的点是:
- 找到对应normalModule类型的moduleFactory,实际上会在对应的dependencyFactories属性中查找对应具体类型的moduleFactory
- dependencyFactories是map结构,key是dependency对应的构造函数,value就是对应的moduleFactory了
- create方法存在回调函数,该回调函数就是产生一个Module对象后相关的处理逻辑,需要重点关注
由于分析源码的实例是单入口的,所以这里对应着SingleEntryDependency的值,即NormalModuleFactory。
NormalModuleFactory中create方法执行逻辑如下:
上面逻辑中需要注意的三点:
- applyPluginsAsyncWaterfall(‘before-resolve’)
执行对应before-resolve事件,执行开始模块解析前相关逻辑
- this.applyPluginsWaterfall0(“factory”, null)
执行对应的factory事件,这里需要提及的是factory是在对应的ModuleFactory中构造函数中注册,例如NormalModuleFactory中
- resolver函数执行
this.applyPluginsWaterfall0(“resolver”, null)执行对应的resolver事件,该事件也是在NormalModuleFactory的构造函数中注册的
这里可以知道resolver就是用来解析模块的,当解析后执行回调函数逻辑,这里需要关注回调逻辑中的NormalModule对象的创建,即模块对象。
createdModule = new NormalModule(
result.request,
result.userRequest,
result.rawRequest,
result.loaders,
result.resource,
result.parser
);
当创建了对应的module对象,最后执行逻辑还是回到最初的moduleFactory.create的回调函数这里(其中关于resolver相关的逻辑之后会具体细看,这里先看整体执行逻辑)。
moduleFactory.create的回调函数中主要逻辑有三点:
- addModule:添加模块到指定地方
- onModule:entry对应模块赋值操作
- buildModule:编译模块
当整体逻辑执行到这里,实际上webpack之后要做的内容就是:
分析模块对象对应的模块的内容并进行优化,即buildModule之后的内容了,这部分内容比较重要也相对复杂,会单独一篇
总结
当Compilation调用run方法后,实际上整个的执行流程主要是:
- run方法执行,before-run和run对应事件执行
- 调用compile实例方法,创建对应的Compilation对象,其中会执行this-compilation和compilation事件对应注册相应的事件以及依赖相关的赋值
- 执行make事件
- 调用addEntry开始模块编译前相关解析工作
- 调用_addModuleChain来构建链式结构
- moduleFactory.create开始生产Module模块对象,生成后执行其回调开始编译模块