【前端必会】tapable、hook,webpack的灵魂( 二 )

每个hook都是一个 tapable包里对应后hook的实例
在回到创建编译器那里,这时创建一个插件的实例,并且执行apply方法,插件就会向自己关系的hook添加事件处理函数(其实还是一个事件监听) , NodeEnvironmentPlugin代码可以自行在源码中查看
new NodeEnvironmentPlugin({infrastructureLogging: options.infrastructureLogging }).apply(compiler);一切都准备好了之后,我们再看一下编译器的run方法
/*** @param {Callback<Stats>} callback signals when the call finishes* @returns {void}*/ run(callback) {if (this.running) {return callback(new ConcurrentCompilationError());}let logger;const finalCallback = (err, stats) => {if (logger) logger.time("beginIdle");this.idle = true;this.cache.beginIdle();this.idle = true;if (logger) logger.timeEnd("beginIdle");this.running = false;if (err) {this.hooks.failed.call(err);}if (callback !== undefined) callback(err, stats);this.hooks.afterDone.call(stats);};const startTime = Date.now();this.running = true;const onCompiled = (err, compilation) => {if (err) return finalCallback(err);if (this.hooks.shouldEmit.call(compilation) === false) {compilation.startTime = startTime;compilation.endTime = Date.now();const stats = new Stats(compilation);this.hooks.done.callAsync(stats, err => {if (err) return finalCallback(err);return finalCallback(null, stats);});return;}process.nextTick(() => {logger = compilation.getLogger("webpack.Compiler");logger.time("emitAssets");this.emitAssets(compilation, err => {logger.timeEnd("emitAssets");if (err) return finalCallback(err);if (compilation.hooks.needAdditionalPass.call()) {compilation.needAdditionalPass = true;compilation.startTime = startTime;compilation.endTime = Date.now();logger.time("done hook");const stats = new Stats(compilation);this.hooks.done.callAsync(stats, err => {logger.timeEnd("done hook");if (err) return finalCallback(err);this.hooks.additionalPass.callAsync(err => {if (err) return finalCallback(err);this.compile(onCompiled);});});return;}logger.time("emitRecords");this.emitRecords(err => {logger.timeEnd("emitRecords");if (err) return finalCallback(err);compilation.startTime = startTime;compilation.endTime = Date.now();logger.time("done hook");const stats = new Stats(compilation);this.hooks.done.callAsync(stats, err => {logger.timeEnd("done hook");if (err) return finalCallback(err);this.cache.storeBuildDependencies(compilation.buildDependencies,err => {if (err) return finalCallback(err);return finalCallback(null, stats);});});});});});};const run = () => {this.hooks.beforeRun.callAsync(this, err => {if (err) return finalCallback(err);this.hooks.run.callAsync(this, err => {if (err) return finalCallback(err);this.readRecords(err => {if (err) return finalCallback(err);this.compile(onCompiled);});});});};if (this.idle) {this.cache.endIdle(err => {if (err) return finalCallback(err);this.idle = false;run();});} else {run();} }https://github.com/webpack/webpack/blob/main/lib/Compiler.js
这里简单分析一下,主要就是执行run相关生命周期 , 以及编译 。并且编译完成后传入回调函数onCompiled
const run = () => {this.hooks.beforeRun.callAsync(this, err => {if (err) return finalCallback(err);this.hooks.run.callAsync(this, err => {if (err) return finalCallback(err);this.readRecords(err => {if (err) return finalCallback(err);this.compile(onCompiled);});});});};整体逻辑不是很复杂,我们主要可以感受到webpack启动后对hook的一些使用方式 。整体的逻辑差不多都是一样的 。是不是很简单 。
总结

  1. 想了解一个框架,一定要找到入口函数,一点一点向前探索 。
  2. tapable 是个好东西,
  3. 关于webpack生命周期,有疑问的时候,除了看文档意外,还可以结合源码去理解,去感受
你学会了吗?欢迎留下你的感受!

推荐阅读