compiler、compilation、NormalModuleFactory、ContextModuleFactory、ResolverFactoy的hooks来源,及tapable核心模块的结构

最近很多人提问webpack中的钩子机制底层是怎样的,本文从模块层面出发,分析一下hooks钩子的传递,以及hook这个底层数据结构的实现。

compiler中的hooks:

源码如下

class Compiler extends Tapable {
	constructor(context) {
		super();
		this.hooks = {
			/** @type {SyncBailHook} */
			shouldEmit: new SyncBailHook(["compilation"]),
			/** @type {AsyncSeriesHook} */
			done: new AsyncSeriesHook(["stats"]),
			/** @type {AsyncSeriesHook<>} */
			additionalPass: new AsyncSeriesHook([]),
			/** @type {AsyncSeriesHook} */
			beforeRun: new AsyncSeriesHook(["compiler"]),
			/** @type {AsyncSeriesHook} */
			run: new AsyncSeriesHook(["compiler"]),
			/** @type {AsyncSeriesHook} */
			emit: new AsyncSeriesHook(["compilation"]),
			/** @type {AsyncSeriesHook} */
			assetEmitted: new AsyncSeriesHook(["file", "content"]),
			/** @type {AsyncSeriesHook} */
			afterEmit: new AsyncSeriesHook(["compilation"]),

			/** @type {SyncHook} */
			thisCompilation: new SyncHook(["compilation", "params"]),
			/** @type {SyncHook} */
			compilation: new SyncHook(["compilation", "params"]),
			/** @type {SyncHook} */
			normalModuleFactory: new SyncHook(["normalModuleFactory"]),
			/** @type {SyncHook}  */
			contextModuleFactory: new SyncHook(["contextModulefactory"]),

			/** @type {AsyncSeriesHook} */
			beforeCompile: new AsyncSeriesHook(["params"]),
			/** @type {SyncHook} */
			compile: new SyncHook(["params"]),
			/** @type {AsyncParallelHook} */
			make: new AsyncParallelHook(["compilation"]),
			/** @type {AsyncSeriesHook} */
			afterCompile: new AsyncSeriesHook(["compilation"]),

			/** @type {AsyncSeriesHook} */
			watchRun: new AsyncSeriesHook(["compiler"]),
			/** @type {SyncHook} */
			failed: new SyncHook(["error"]),
			/** @type {SyncHook} */
			invalid: new SyncHook(["filename", "changeTime"]),
			/** @type {SyncHook} */
			watchClose: new SyncHook([]),

			/** @type {SyncBailHook} */
			infrastructureLog: new SyncBailHook(["origin", "type", "args"]),

			// TODO the following hooks are weirdly located here
			// TODO move them for webpack 5
			/** @type {SyncHook} */
			environment: new SyncHook([]),
			/** @type {SyncHook} */
			afterEnvironment: new SyncHook([]),
			/** @type {SyncHook} */
			afterPlugins: new SyncHook(["compiler"]),
			/** @type {SyncHook} */
			afterResolvers: new SyncHook(["compiler"]),
			/** @type {SyncBailHook} */
			entryOption: new SyncBailHook(["context", "entry"])
		};
    }
}

compilation中的hooks:

源码如下,从源码可以看出compilation在初始化的时候传入了compiler。

class Compilation extends Tapable {
	/**
	 * Creates an instance of Compilation.
	 * @param {Compiler} compiler the compiler which created the compilation
	 */
	constructor(compiler) {
		super();
		this.hooks = {
			/** @type {SyncHook} */
			buildModule: new SyncHook(["module"]),
			/** @type {SyncHook} */
			rebuildModule: new SyncHook(["module"]),
			/** @type {SyncHook} */
			failedModule: new SyncHook(["module", "error"]),
			/** @type {SyncHook} */
			succeedModule: new SyncHook(["module"]),

			/** @type {SyncHook} */
			addEntry: new SyncHook(["entry", "name"]),
			/** @type {SyncHook} */
			failedEntry: new SyncHook(["entry", "name", "error"]),
			/** @type {SyncHook} */
			succeedEntry: new SyncHook(["entry", "name", "module"]),

			/** @type {SyncWaterfallHook} */
			dependencyReference: new SyncWaterfallHook([
				"dependencyReference",
				"dependency",
				"module"
			]),

			/** @type {AsyncSeriesHook} */
			finishModules: new AsyncSeriesHook(["modules"]),
			/** @type {SyncHook} */
			finishRebuildingModule: new SyncHook(["module"]),
			/** @type {SyncHook} */
			unseal: new SyncHook([]),
			/** @type {SyncHook} */
			seal: new SyncHook([]),

			/** @type {SyncHook} */
			beforeChunks: new SyncHook([]),
			/** @type {SyncHook} */
			afterChunks: new SyncHook(["chunks"]),

			/** @type {SyncBailHook} */
			optimizeDependenciesBasic: new SyncBailHook(["modules"]),
			/** @type {SyncBailHook} */
			optimizeDependencies: new SyncBailHook(["modules"]),
			/** @type {SyncBailHook} */
			optimizeDependenciesAdvanced: new SyncBailHook(["modules"]),
			/** @type {SyncBailHook} */
			afterOptimizeDependencies: new SyncHook(["modules"]),

			/** @type {SyncHook} */
			optimize: new SyncHook([]),
			/** @type {SyncBailHook} */
			optimizeModulesBasic: new SyncBailHook(["modules"]),
			/** @type {SyncBailHook} */
			optimizeModules: new SyncBailHook(["modules"]),
			/** @type {SyncBailHook} */
			optimizeModulesAdvanced: new SyncBailHook(["modules"]),
			/** @type {SyncHook} */
			afterOptimizeModules: new SyncHook(["modules"]),

			/** @type {SyncBailHook} */
			optimizeChunksBasic: new SyncBailHook(["chunks", "chunkGroups"]),
			/** @type {SyncBailHook} */
			optimizeChunks: new SyncBailHook(["chunks", "chunkGroups"]),
			/** @type {SyncBailHook} */
			optimizeChunksAdvanced: new SyncBailHook(["chunks", "chunkGroups"]),
			/** @type {SyncHook} */
			afterOptimizeChunks: new SyncHook(["chunks", "chunkGroups"]),

			/** @type {AsyncSeriesHook} */
			optimizeTree: new AsyncSeriesHook(["chunks", "modules"]),
			/** @type {SyncHook} */
			afterOptimizeTree: new SyncHook(["chunks", "modules"]),

			/** @type {SyncBailHook} */
			optimizeChunkModulesBasic: new SyncBailHook(["chunks", "modules"]),
			/** @type {SyncBailHook} */
			optimizeChunkModules: new SyncBailHook(["chunks", "modules"]),
			/** @type {SyncBailHook} */
			optimizeChunkModulesAdvanced: new SyncBailHook(["chunks", "modules"]),
			/** @type {SyncHook} */
			afterOptimizeChunkModules: new SyncHook(["chunks", "modules"]),
			/** @type {SyncBailHook} */
			shouldRecord: new SyncBailHook([]),

			/** @type {SyncHook} */
			reviveModules: new SyncHook(["modules", "records"]),
			/** @type {SyncHook} */
			optimizeModuleOrder: new SyncHook(["modules"]),
			/** @type {SyncHook} */
			advancedOptimizeModuleOrder: new SyncHook(["modules"]),
			/** @type {SyncHook} */
			beforeModuleIds: new SyncHook(["modules"]),
			/** @type {SyncHook} */
			moduleIds: new SyncHook(["modules"]),
			/** @type {SyncHook} */
			optimizeModuleIds: new SyncHook(["modules"]),
			/** @type {SyncHook} */
			afterOptimizeModuleIds: new SyncHook(["modules"]),

			/** @type {SyncHook} */
			reviveChunks: new SyncHook(["chunks", "records"]),
			/** @type {SyncHook} */
			optimizeChunkOrder: new SyncHook(["chunks"]),
			/** @type {SyncHook} */
			beforeChunkIds: new SyncHook(["chunks"]),
			/** @type {SyncHook} */
			optimizeChunkIds: new SyncHook(["chunks"]),
			/** @type {SyncHook} */
			afterOptimizeChunkIds: new SyncHook(["chunks"]),

			/** @type {SyncHook} */
			recordModules: new SyncHook(["modules", "records"]),
			/** @type {SyncHook} */
			recordChunks: new SyncHook(["chunks", "records"]),

			/** @type {SyncHook} */
			beforeHash: new SyncHook([]),
			/** @type {SyncHook} */
			contentHash: new SyncHook(["chunk"]),
			/** @type {SyncHook} */
			afterHash: new SyncHook([]),
			/** @type {SyncHook} */
			recordHash: new SyncHook(["records"]),
			/** @type {SyncHook} */
			record: new SyncHook(["compilation", "records"]),

			/** @type {SyncHook} */
			beforeModuleAssets: new SyncHook([]),
			/** @type {SyncBailHook} */
			shouldGenerateChunkAssets: new SyncBailHook([]),
			/** @type {SyncHook} */
			beforeChunkAssets: new SyncHook([]),
			/** @type {SyncHook} */
			additionalChunkAssets: new SyncHook(["chunks"]),

			/** @type {AsyncSeriesHook} */
			additionalAssets: new AsyncSeriesHook([]),
			/** @type {AsyncSeriesHook} */
			optimizeChunkAssets: new AsyncSeriesHook(["chunks"]),
			/** @type {SyncHook} */
			afterOptimizeChunkAssets: new SyncHook(["chunks"]),
			/** @type {AsyncSeriesHook} */
			optimizeAssets: new AsyncSeriesHook(["assets"]),
			/** @type {SyncHook} */
			afterOptimizeAssets: new SyncHook(["assets"]),

			/** @type {SyncBailHook} */
			needAdditionalSeal: new SyncBailHook([]),
			/** @type {AsyncSeriesHook} */
			afterSeal: new AsyncSeriesHook([]),

			/** @type {SyncHook} */
			chunkHash: new SyncHook(["chunk", "chunkHash"]),
			/** @type {SyncHook} */
			moduleAsset: new SyncHook(["module", "filename"]),
			/** @type {SyncHook} */
			chunkAsset: new SyncHook(["chunk", "filename"]),

			/** @type {SyncWaterfallHook} */
			assetPath: new SyncWaterfallHook(["filename", "data"]), // TODO MainTemplate

			/** @type {SyncBailHook} */
			needAdditionalPass: new SyncBailHook([]),

			/** @type {SyncHook} */
			childCompiler: new SyncHook([
				"childCompiler",
				"compilerName",
				"compilerIndex"
			]),

			/** @type {SyncBailHook} */
			log: new SyncBailHook(["origin", "logEntry"]),

			// TODO the following hooks are weirdly located here
			// TODO move them for webpack 5
			/** @type {SyncHook} */
			normalModuleLoader: new SyncHook(["loaderContext", "module"]),

			/** @type {SyncBailHook} */
			optimizeExtractedChunksBasic: new SyncBailHook(["chunks"]),
			/** @type {SyncBailHook} */
			optimizeExtractedChunks: new SyncBailHook(["chunks"]),
			/** @type {SyncBailHook} */
			optimizeExtractedChunksAdvanced: new SyncBailHook(["chunks"]),
			/** @type {SyncHook} */
			afterOptimizeExtractedChunks: new SyncHook(["chunks"])
        };
    }
}

NormalModuleFactory中的hooks:源码如下

class NormalModuleFactory extends Tapable {
	constructor(context, resolverFactory, options) {
		super();
		this.hooks = {
			resolver: new SyncWaterfallHook(["resolver"]),
			factory: new SyncWaterfallHook(["factory"]),
			beforeResolve: new AsyncSeriesWaterfallHook(["data"]),
			afterResolve: new AsyncSeriesWaterfallHook(["data"]),
			createModule: new SyncBailHook(["data"]),
			module: new SyncWaterfallHook(["module", "data"]),
			createParser: new HookMap(() => new SyncBailHook(["parserOptions"])),
			parser: new HookMap(() => new SyncHook(["parser", "parserOptions"])),
			createGenerator: new HookMap(
				() => new SyncBailHook(["generatorOptions"])
			),
			generator: new HookMap(
				() => new SyncHook(["generator", "generatorOptions"])
			)
		};
    }
}

ContextModuleFactory

class ContextModuleFactory extends Tapable {
	constructor(resolverFactory) {
		super();
		this.hooks = {
			/** @type {AsyncSeriesWaterfallHook} */
			beforeResolve: new AsyncSeriesWaterfallHook(["data"]),
			/** @type {AsyncSeriesWaterfallHook} */
			afterResolve: new AsyncSeriesWaterfallHook(["data"]),
			/** @type {SyncWaterfallHook} */
			contextModuleFiles: new SyncWaterfallHook(["files"]),
			/** @type {SyncWaterfallHook} */
			alternatives: new AsyncSeriesWaterfallHook(["modules"])
		};
		this._pluginCompat.tap("ContextModuleFactory", options => {
			switch (options.name) {
				case "before-resolve":
				case "after-resolve":
				case "alternatives":
					options.async = true;
					break;
			}
		});
		this.resolverFactory = resolverFactory;
	}
}

ResolverFactoy

class ResolverFactory extends Tapable {
	constructor() {
		super();
		this.hooks = {
			resolveOptions: new HookMap(
				() => new SyncWaterfallHook(["resolveOptions"])
			),
			resolver: new HookMap(() => new SyncHook(["resolver", "resolveOptions"]))
		};
}

tapable实现结构原理:

compiler, compilation, NormalModuleFactory均继承自tapable模块,tapable模块提供了很多钩子函数的基类。如图:

compiler、compilation、NormalModuleFactory、ContextModuleFactory、ResolverFactoy的hooks来源,及tapable核心模块的结构_第1张图片

compiler、compilation、NormalModuleFactory、ContextModuleFactory、ResolverFactoy的hooks来源,及tapable核心模块的结构_第2张图片

 

说明:

这里面hooks,HookCodeFactory是其他钩子函数的基类,后面在webpack中订阅的事件最终都会放入taps栈中,订阅分为三种类型async, sync, promise,调用时会通过_createCall方法调用HookCodeFactory的create方法创建委托函数。

从源码中还可以看出,所有的hooks钩子都有两个类,一个是钩子,一个是钩子工厂。

钩子继承自hook类,钩子工厂继承自HookCodeFactory。

HookMap,MultiHook也是两个独立的类,用来操作hook类

关于tapable的实现原理详解参见基于数据结构从源码解析webpack核心模块tapable

 

你可能感兴趣的:(webpack,node)