原文首發於 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
來決定程序走向。