(源码篇)浅析webpack5中Compiler中重要的hook调用过程

今天的内容只有一个:浅析webpack5中Compiler中重要的hook调用。

1. 浅析Compiler 中的第一个核心hook 是 compile

此hook的入参是 params

1.1 查看绑定 compile 的插件的数据

进入此hook,发现只有一个监听的插件:ExternalModuleFactoryPlugin

进入 ExternalModuleFactoryPlugin的插件中,代码如下:

class ExternalsPlugin {
	/**
	 * @param {string | undefined} type default external type
	 * @param {Externals} externals externals config
	 */
	constructor(type, externals) {
		this.type = type;
		this.externals = externals;
	}

	/**
	 * Apply the plugin
	 * @param {Compiler} compiler the compiler instance
	 * @returns {void}
	 */
	apply(compiler) {
		compiler.hooks.compile.tap("ExternalsPlugin", ({ normalModuleFactory }) => {
			new ExternalModuleFactoryPlugin(this.type, this.externals).apply(
				normalModuleFactory
			);
		});
	}
}

发现此插件 又实例化了 一个 ExternalModuleFactoryPlugin 的对象,但是 apply 方法上 传入的是 normalModuleFactory 数据。聪明的小伙伴可以想一下此处是为了啥?
进入 ExternalModuleFactoryPlugin 内部,看一下其内部实现是什么?代码如下:

会发现这个插件实际上也就是监听了一下 normalModuleFactory.hooks.factorize 的事件。

1.2 总结

总体来说:Compiler 里的 hooks.compile 的主要作用就是通过 ExternalModuleFactoryPlugin 监听了 normalModuleFactory.hooks.factorize 的事件。

2. 继续调试 Compilercompile hook

2.1 创建 compilation 天选打工人

截图奉上 下一步的逻辑

会发现走到了 newCompilation 方法,通过规范的命名就知道,此方法是创建一个此次编译构建用的 compilation 一次性对象,注意此处说的是一次性对象,表述上可能存在差异,给兄弟们放上解释。【我感觉可以简单理解为一次完整编译构建过程中的数据载体】。

具体newCompilation代码如下:


newCompilation(params) {
		const compilation = this.createCompilation(params); // 下方
		compilation.name = this.name;
		compilation.records = this.records;
		// 调用 thisCompilation hook
		this.hooks.thisCompilation.call(compilation, params);
		// 调用 compilation hook
		this.hooks.compilation.call(compilation, params);
		return compilation;
}
  
  
createCompilation(params) {
		this._cleanupLastCompilation();
		return (this._lastCompilation = new Compilation(this, params));
}

核心也就是在这里了,通过 createCompilation 传递 compiler 和 params参数,创建 Compilation 的实例对象。

创建完毕以后,开始 传入 compilation 和 params 调用 hooks.thisCompilation的hook了。

下一步就是开始调试 thisCompilation 的 hook。

3. Compiler 创建了 compilation,开始调用 thisCompilation 的 hook

3.1 查看绑定 compilation 的插件的数据

继续 调试进入此hook

这个hook竟然有 10个 插件监听它,先进入第一个插件 ArrayPushCallbackChunkFormatPlugin

发现这个插件都是在给 compilation 绑定一下 监听事件,并没有做什么实际的操作,继续下一个插件JsonpChunkLoadingPlugin

进入以后,发现也是在进行给 compilation 绑定监听事件。直接进入 最后一个插件ResolverCachePlugin

发现也是在给 compilation 绑定一堆的监听事件。

好家伙,这是要把 compilation 给累死呀,

3.2 thisCompilation 的 总结

总结:Compiler 中的 thisCompilation 的 hook,就是在疯狂的给 compilation 通过各种插件 挂各种的 监听事件,弹药准备完毕,等待一触即发。

compilation内心想法:我可真是太难了。。。)

4. Compiler 继续 触发 compilation 的 hook

4.1 查看绑定compilation的插件的数据

继续进行下一步 调试。

我直接疯了啊,这Compilercompilation 的 hook 竟然绑定了 54 个监听的插件。

我 giao哥 直接疯掉。而我和大家作为优秀的 程序员,那不就是多点54次调试吗?
进入第一个插件 ChunkPrefetchPreloadPlugin

4.2 进入 ChunkPrefetchPreloadPlugin 插件

这又是给 compilation 对象 【注意不是hook名称】,绑定了一身的 监听事件。

继续看下一个插件的,下一个插件是 JavascriptModulesPlugin

4.3 进入 JavascriptModulesPlugin 插件

JavascriptModulesPlugin 的代码如下:

看到了上面的一部分,compilation 对象终于开心了,终于不仅仅是给我绑定监听事件了,这次轮到了 normalModuleFactory,心里美滋滋。

继续看下面一部分代码,

去求吧,compilation 对象还是逃不过被绑定监听事件的命运,【注意此处的hook名称是不同的】。

继续进入下一个插件

4.4 进入 JsonModulesPlugin 插件

normalModuleFactory 绑定 监听事件

4.5 进入 AssetModulesPlugin 插件

normalModuleFactorycompilation 绑定 监听事件

4.6 进入 EntryPlugin 插件

这个插件 终于不绑定事件了,仅仅是 向 compilation.dependencyFactories 中塞入了一对数据。

4.7 进入 RuntimePlugin 插件 【核心】

这个插件的代码量是真的多,看名字分析这个插件应该是处理运行时的数据

简单看下里面的部分内容:

4.7 进入 InferAsyncModulesPlugin 插件

compilation 绑定 监听事件

4.8 直接干到最后一个 WarnCaseSensitiveModulesPlugin 插件

也是在给 辛苦的 compilation 的身上 挂载监听事件。

4.9 总结

总的来说, Compiler 触发 compilation 的 hook 本质上是给我们辛勤的打工人 compilation对象 的 不同的数据处理阶段 绑定不同的插件。

5 继续 Compiler 的下一个hook make

走完 this.newCompilation(params); 的调用流程后,下一步就是调用 Compilermake 的hook了。

5.1 查看绑定make的插件的数据

nice 的很,这个 hook 仅仅 有一个叫 EntryPlugin 的插件进行绑定。

5.2 进入 EntryPlugin 插件 【 compilation 对象解析的开始】

核心代码截图如下:

你会发现这个插件就是调用了 compilation.addEntry 的方法,没有做其他逻辑。那就开始分析此函数的入参,context, dep, options
查看一下入参的数据,如下图

你就会显而易见的发现 dep 这个是主角了,那 dep 又是由 const dep = EntryPlugin.createDependency(entry, options); 创建的,查看 createDependency 静态方法:

static createDependency(entry, options) {
		// 创建了 EntryDependency 继承自 ModuleDependency 继承自 Dependency (抽象类)
		const dep = new EntryDependency(entry);
		// TODO webpack 6 remove string option
		dep.loc = { name: typeof options === "object" ? options.name : options };
		return dep;
	}

直接看 这个 dep 对象的数据 都有啥:


webpack.config.js 配置如下:

是不是看到了,熟悉的webpack 中的 entry 的路径 和 此 dep 对象的 request 属性是一致的呢?

5.3 进入 compilation 中 查看,addEntry 方法

上代码

addEntry(context, entry, optionsOrName, callback) {
		console.log("add entry");
		// TODO webpack 6 remove
		const options =
			typeof optionsOrName === "object"
				? optionsOrName
				: { name: optionsOrName };

		this._addEntryItem(context, entry, "dependencies", options, callback);
	}

此函数仅仅是做了数据处理,真正干活的还在 this._addEntryItem 函数中,进入 this._addEntryItem函数

你会发现此函数 调用了 this.hooks.addEntry【注意此时的 this 指的的是 compilation 对象】,进入此 hook

并没有插件 监听它,直接进入下一行代码:调用this.addModuleTree 方法。

5.4 进入 this.addModuleTree 的方法

处理完数据以后,又进入一个 this.handleModuleCreation 的方法。

5.5 进入this.handleModuleCreation 的方法

又进入 this.factorizeModule 的方法

5.6 进入 this.factorizeModule 的方法

// Workaround for typescript as it doesn't support function overloading in jsdoc within a class
Compilation.prototype.factorizeModule = /** @type {{
	(options: FactorizeModuleOptions & { factoryResult?: false }, callback: ModuleCallback): void;
	(options: FactorizeModuleOptions & { factoryResult: true }, callback: ModuleFactoryResultCallback): void;
}} */ (
	function (options, callback) {
		console.log("add entry to factorize Queue, real job start");
		this.factorizeQueue.add(options, callback);
	}
);

你会发现它是在Compilation 的原型上绑定的一个方法,其主要的作用就是 向 factorizeQueue 中添加了一个数据。然后就么有然后了????

老规矩:总结

  1. Compiler 里的 hooks.compile 的主要作用就是通过 ExternalModuleFactoryPlugin 监听了 normalModuleFactory.hooks.factorize 的事件。
  2. Compilercompile hook 传递 compiler 和 params参数 给 createCompilation 方法,创建 Compilation 的实例对象 compilation
  3. Compiler 中的 thisCompilation 的 hook,就是在疯狂的给 compilation对象 通过各种插件 挂各种的 监听事件,弹药准备完毕,等待一触即发
  4. Compiler 中的 compilation 的 hook 本质上也是给我们辛勤的打工人 compilation对象的不同的数据处理阶段,绑定不同的插件。
  5. Compiler中的 make 的hook,主要是 通过 EntryPlugin 插件,解析我们传入的entry属性,并调用 compilation.addEntry 的方法,进而通过一系列的数据处理,最后将数据塞入到 compilationfactorizeQueue 中。

在一个类似 队列的数据中添加了,就没有然后了吗?到底怎么开始启动的呢?怎么没有见到 compilation 调用之前的hook呢?

其实上面的疑问都会在下一篇的 webpack5 中的 任务调度中,进行讲解,敬请期待。

如果有忍不住的小伙伴们,也可以自己去调试一下webpack5 相关的源码,欢迎一起学习交流。兄弟们下篇文章再见。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值