webpack版本为5.74.0
基本配置如下图:
首先在webpack.js文件中可以看出先获取自己配置的options传入createCompiler方法。
我们进入该方法看看:
又会调用getNormalizedWebpackOptions方法,我们继续进入该方法看看:
可以看到会拿到config进行赋值,没有传入的参数会给一个默认值。
在84行还会初始化一些其他参数。该阶段可以看出只是设置options的默认
从下图可以看出接下来会初始化compiler对象:
我们进入该类看看:
可以看出只是初始化一些参数。
createCompiler方法后面先是执行了plugin,然后初始化了options。然后执行applyWebpackOptionsDefaults,
该方法还是给options初始化一些参数。我们继续看到new WebpackOptionsApply().process(options, compiler)。
此时进入看看:
先是赋值compiler的部分参数,然后根据配置的一些参数自动引入一些插件
比如options.output.clean,设置的clean参数就会自动引入CleanPlugin插件。
后面的代码就全是根据options的配置设置plguin。执行完该函数就会执行compiler的run方法开始执行编译:
所以该阶段就是根据options部分初始化compiler,执行自己配置的plugins,随后根据配置加载系统自带的插件。
执行run方法会执行compile方法:
首先会执行初始化compilation需要的参数:
随后生成compilation,
执行createCompilation:
初始化compilation:
进入make阶段:
执行make事件的回调,由于在EntryPlugin.js里监听了make方法,所以执行compilation的addEntry方法添加入口依赖:
最终是在_entries里添加了依赖:
然后执行:
在获取到入口依赖(当前模块名对象)和模块创造函数moduleFactory(用来生成模块)后执行handleModuleCreation来根据入口文件开始建立模块间的依赖关系:
factorizeQueue是异步队列,该队列会执行processor函数,会将this.factorizeQueue的第一个参数传个processor。执行完后将结果传给this.factorizeQueue函数的回调。
this.factorizeQueue = new AsyncQueue({
name: "factorize",
parent: this.addModuleQueue,
processor: this._factorizeModule.bind(this)
});
该异步队列主要执行_factorizeModule,_factorizeModule中的创造函数就是我们入口依赖对应的创造函数(NormalModuleFactory是创造普通模块)。我们看看该函数的create方法:
这里订阅了一个beforeResolve的钩子,该钩子的作用是webpack在解析url的时候会先调用该钩子。callAsync钩子在函数有返回值时就会跳过后续函数,直接执行callAsync
等触发函数绑定的回调函数。最后生成模块后回调。
_factorizeModule主要是执行factory.create方法去生成下图所示内容:
此时执行factorizeModule的回调函数我们就开始进入编译模块。
添加了入口依赖后会执行一下函数:
会在addModuleQueue添加当前模块和回调函数,这是个异步对列,具体实现内容看我这篇内容异步队列实现。
addModule的回调函数主要执行moduleGraph.setResolvedModule方法去设置moduleGraph的_dependencyMap,该map记录了当前导入或导出dependency所对应的模块直接incoming和outgoing之间的关系。如下图所示,当前dependency如果有ModuleGraphConnection那么就表示当前dependency是有被用到的。这个在treeshaking中会有用。
然后调用_handleModuleBuildAndDependencies方法:
该方法调用buildModule方法:
buildModule方法主要是在异步调度队列中添加该模块和回调函数:
asyncQueue有个processor就是异步调度要执行的函数,看出会执行_buildModule方法,并且会把该方法执行结果回调给1334行的callback函数
在该函数中会执行needBuild方法
然后调用module.build方法
该方法会调用模块的_doBuild方法:
该方法会调用runLoaders方法来执行loaders,执行loaders的方法是独立于webpack文件的,调用的是loader-runner文件,处理完会执行runLoaders的回调,将结果带过来,执行完后会执行_doBuild的callback:
执行完runLoaders的回调中主要执行processResult方法:
然后执行_doBuild的回调主要执行parse方法去解析当前模块的依赖和导出:
这里将当前的buffer转换成string,并通过parse方法中的JavascriptParser._parse方法解析成ast:
然后通过执行blockPreWalkStatements查找当前模块引入的依赖,通过walkStatements查找出自身的导出模块。并设置在module.dependencies:
执行了loaders后再解析模块依赖,此时buildModule执行完毕,然后执行最开始的buildModule回调会处理模块的依赖:
这也是一个asyncQueue,会执行_processModuleDependencies方法:
引入图如下(带方框的是异步引入的模块):
_processModuleDependencies主要调用如下方法循环处理依赖:
在执行processDependency函数中会设置当前导出对应的模块和当前导出缓存:
执行完后会执行:
该方法中又执行了handleModuleCreation方法,又循环执行开始的this.factorizeModule方法直到解析完所有的依赖。
看完这篇继续下一篇:
带你从源码看看webpack 的构建流程(下)?