webpack作为前端打包的重要工具之一,我们有必要深入掌握webpack的底层数据结构。官方文档上直接给出了webpack的核心模块tapable,但是并没有给出特别详细的介绍其实现原理和内部实现,本文从源码触发,来解析下tapable的数据结构和方法属性,这对于掌握webpack打包机制的理解是很有帮助的。
两个集合,taps集合,interceptors集合,并定义了三个操作tap的方法tap, tapAsync, tapPromise,对应的还有三个创建委托函数(将监听函数转化为字符串)的方法_call, _promise, _callAsync
各种钩子的工厂类基础类,每个钩子的工厂类会继承HookCodeFactory。比如syncHook
const Hook = require("./Hook"); const HookCodeFactory = require("./HookCodeFactory"); class SyncHookCodeFactory extends HookCodeFactory { content({ onError, onDone, rethrowIfPossible }) { return this.callTapsSeries({ onError: (i, err) => onError(err), onDone, rethrowIfPossible }); } } const factory = new SyncHookCodeFactory(); class SyncHook extends Hook { tapAsync() { throw new Error("tapAsync is not supported on a SyncHook"); } tapPromise() { throw new Error("tapPromise is not supported on a SyncHook"); } compile(options) { factory.setup(this, options); return factory.create(options); } } module.exports = SyncHook;说明:各种钩子继承Hook类之后,会定义compile方法,该方法调用了工厂类实例,最终和HookCodeFactory联系起来,在各种钩子上去执行HookCodeFactory的方法
HookCodeFactory的方法:
主要实现了串行,并行,循环,执行单个监听函数的方法,也就是taps出栈的方法
可以看出,在hookMap内定义了字典的数据结构来存储hook实例
for(key) { const hook = this.get(key); if (hook !== undefined) { return hook; } let newHook = this._factory(key); const interceptors = this._interceptors; for (let i = 0; i < interceptors.length; i++) { newHook = interceptors[i].factory(key, newHook); } this._map.set(key, newHook); return newHook; }for方法将传入的factory的hook设置到字典
批量操作hooks
其余的钩子函数都具有类似性,根据串行,并行或者其他在钩子工厂实例的content方法中执行不同的HookCodeFactory方法
比如:
总结:
同步钩子:
SyncBailHook:类似于 SyncHook,执行过程中注册的回调返回非 undefined 时就停止不在执行。SyncWaterfallHook:接受至少一个参数,上一个注册的回调返回值会作为下一个注册的回调的参数。SyncLoopHook:有点类似 SyncBailHook,但是是在执行过程中回调返回非 undefined 时继续再次执行当前的回调。异步钩子与之相同,这里不再阐述具体实现原理。
参考:https://segmentfault.com/a/1190000020146256
https://www.cnblogs.com/Darlietoothpaste/p/10474871.html