原文首发于 blog.flqin.com。若有错误,请联系笔者。分析码字不易,转载请代表出处,谢谢!webpack
webpack/lib/Compiler.js
该文件是 webpack
的核心, Compiler
类定义了整个构建的流程,然后文的 Compilation
类则负责如何具体去构建;new Compiler
执行 constructor
,首先扩展了 Tapable
,在 constructor
里定义了一堆钩子 done,beforeRun,run,emit
等等;this._pluginCompat.tap("Compiler")
,这个用来兼容以前的老版 webpack
的 plugin
的钩子,触发时机在tapable/lib/Tapable.js
里调用plugin
的时候;Compiler
类的 run
即为整个打包的主流程函数;继续执行 webpack.js
,执行:web
new NodeEnvironmentPlugin({
infrastructureLogging: options.infrastructureLogging
}).apply(compiler);
复制代码
该类主要对文件系统作了一些封装,包括输入,输出,缓存,监听
等等,这些扩展后的方法所有挂载在 compiler
对象下。json
而后对本身 config
文件里的 plugins
进行了注册:数组
if (options.plugins && Array.isArray(options.plugins)) {
for (const plugin of options.plugins) {
if (typeof plugin === 'function') {
plugin.call(compiler, compiler);
} else {
plugin.apply(compiler);
}
}
}
复制代码
在这里,会把 compiler
实例传进去供 plugin
使用,compiler
包含整个构建流程的所有钩子,经过它能够把控整个 webpack
构建周期。缓存
掌握流程里各对象(如 compiler
,compilation
)的事件钩子触发的时机,就是掌握如何写一个插件的关键。如何写一个 webpack 插件?app
接着触发了 compiler
的 hooks
: environment,afterEnvironment
,而后执行:函数
compiler.options = new WebpackOptionsApply().process(options, compiler);
复制代码
该 WebpackOptionsApply
类的 process
除了把配置里的一些属性添加到 compiler
对象下,更主要的是根据 options
的配置不一样,注册激活一些默认自带的插件和 resolverFactory.hooks
,大部分插件的做用是往 compiler.hooks:compilation,thisCompilation
里注册一些事件(此时该钩子已经获取到 normalModuleFactory
等参数),如:ui
new JavascriptModulesPlugin().apply(compiler); //给normalModuleFactory的js模块提供Parser、JavascriptGenerator对象 ,并给seal阶段的template提供renderManifest数组(包含render方法)
new JsonModulesPlugin().apply(compiler); //给normalModuleFactory的json模块提供Parser、JavascriptGenerator对象
new WebAssemblyModulesPlugin({
mangleImports: options.optimization.mangleWasmImports
}).apply(compiler); // 同理,webassembly模块
复制代码
new EntryOptionPlugin().apply(compiler);
compiler.hooks.entryOption.call(options.context, options.entry); //建立多入口仍是单入口 SingleEntryPlugin | MultiEntryPlugin,二者均会在 apply 方法里注册 Compiler.hooks:compilation, make
复制代码
插件处理完毕后,触发 compiler.hooks
: afterPlugins
。this
compiler.resolverFactory.hooks.resolveOptions.for('normal').tap('WebpackOptionsApply', resolveOptions => {
return Object.assign(
{
fileSystem: compiler.inputFileSystem
},
cachedCleverMerge(options.resolve, resolveOptions) //配置项 options.resolve
);
});
compiler.resolverFactory.hooks.resolveOptions.for('context').tap('WebpackOptionsApply', resolveOptions => {
return Object.assign(
{
fileSystem: compiler.inputFileSystem,
resolveToContext: true
},
cachedCleverMerge(options.resolve, resolveOptions) //配置项 options.resolve
);
});
compiler.resolverFactory.hooks.resolveOptions.for('loader').tap('WebpackOptionsApply', resolveOptions => {
return Object.assign(
{
fileSystem: compiler.inputFileSystem
},
cachedCleverMerge(options.resolveLoader, resolveOptions) //配置项 options.resolveLoader
);
});
复制代码
而后注册 compiler.resolverFactory.hooks: resolveOptions for (normal/context/loader)
,目的是为 Factory.createResolver
提供默认的参数对象(含有相关的 resolve
项目配置项)。spa
注册完成后,触发 compiler.hooks
: afterResolvers
,到此 compiler
初始化完毕。
回到cli.js
,处理配置项 progress
和 infoVerbosity
,而后判断 options
里是否有 watch
,有则走 compiler.watch
,无则走 compiler.run
,这里咱们走compiler.run
,进入 webpack
核心构建流程!
Compiler
,它扩展于Tapable
,是 webapck
的核心;FS
,而后注册了 plugins
,包括项目配置的和项目默认的;resolverFactory.hooks
用于 Factory.createResolver
方法提供参数对象;watch
来决定程序走向。