tapable
1|0tapable
what: webpack 事件流机制的核心库。
why : 方便 webpack 运行的各生命周期中扩展功能。
how : 核心原理依赖于发布订阅模式,类似于 nodejs 的 events 库。
1|1基础使用
- tapable 库导出的是各种钩子的集合
- 创建钩子实例的时候传参必须为数组,数组内元素为注册函数的形参
- hook 分别有注册和响应的函数
- 注册的第一个参数名为了方便代码阅读和维护,对实际运行没有影响
- 响应函数的参数,作为注册函数的实参运行
1|2Hook 的种类
Hook 最主要可分为同步和异步钩子两大类,异步钩子还可细分为串行和并行两类。
-
同步:
-
SyncHook :
是所有 hook 中最普通、功能最纯粹的,每个注册的函数都将按照注册的顺序执行。
所有执行函数参数相同。
call 函数无返回值。 -
SyncBailHook :
带 Bail 的 hook 的特点是允许注册的函数停止后续函数的执行。当前函数的返回值不为
undefined
则会阻止后续函数的执行。
所有执行函数参数相同。
call 函数返回值为最后一个执行函数的返回值。 -
SyncWaterfallHook :
带 Waterfall 的 hook 的特点是当前执行函数的返回值作为下一个函数执行的第一个参数。所有注册函数依旧按照注册顺序执行。
执行函数的参数数量相同,但第一个参数可能是上一个函数的返回值。
call 函数返回的是最后一个有返回值函数的返回值。 -
SyncLoopHook :
带 Loop 的 hook 的特点是当前执行函数返回值不为undefined
时,当前函数将循环执行。所有执行函数参数相同。
call 函数无返回值。
该 hook 并没有在 webpack 核心库使用,可根据业务需求做插件扩展。
-
-
异步 :
带 Async 的 hook 的均为异步钩子。
异步钩子增加了两种注册和执行函数。(异步钩子也可以使用同步注册和执行)
tapAsync
注册的函数通过callAsync
执行。
tapAsync
注册的函数的参数最后增加一个回调函数,该回调函数的执行用来通知 hook 当前函数执行结束。
当tapAsync
参数不为undefined
/null
时,视为出现异常,callAsync
的回调函数会将次异常作为参数立即执行,往后其余注册函数的回调将不影响最终回调函数的执行。
callAsync
的最后一个参数也增加一个回调函数,当所有注册函数的回调执行完成后,该回调函数最后执行。tapPromise
注册的函数通过promise
执行。
tapPromise
注册的函数相当于 一个 Promise 对象。
promise
执行函数的效果相当于 Promise.all 的执行。
关于注册函数出现 reject 的情况根据 与相同 hook 的tapAsync
出现异常情况相同。-
AsyncParallelHook :
带 Parallel 的 hook 的均为异步并行钩子,其特点是各个注册函数之间的执行互不影响。此处演示
tapAsync
回调执行异常的情况。
即便出现异常也不影响后置函数的执行。 -
AsyncParallelBailHook :
与 AsyncParallelHook 区别在于:
AsyncParallelHook 的callAsync
的回调函数的异常参数为第一个执行时出现异常的函数。
AsyncParallelBailHook 的callAsync
的回调函数的异常参数为出现异常的函数中注册顺序最靠前的函数。 -
AsyncSeriesHook :
带 Series 的 hook 的均为异步串行钩子,其特点是前置注册函数执行完成之后,后置的注册函数才开始执行。
当执行回调出现异常时,将阻断后置函数的执行,异常作为callAsync
的参数执行。 -
AsyncSeriesBailHook / AsyncSeriesWaterfallHook / AsyncSeriesLoopHook :结合以上关键字的特性可得特性。
-
总结:
关键字 | 含义 |
---|---|
Sync | 同步钩子,注册函数按注册顺序同步执行 |
tap/call | 同步注册 /执行 |
Async | 异步钩子,支持异步执行的注册函数 |
tapAsync/callAsync | 异步,通过回调函数通知钩子执行结束,回调函数有传参视为抛出异常处理 |
tapPromise/promise | 异步,按照 Promise 的规则运行 |
Parallel | 异步并行,注册函数并行执行 |
Series | 异步串行,注册函数串行执行 |
Bail | 保险钩子,前置注册函数的有返回值时,后置注册函数将不执行 |
Waterfall | 瀑布流钩子,前置注册函数的返回值将作为后置函数的参数 |
Loop | 循环钩子,当注册函数有返回值时,将会再次执行 |
AsyncParallelBailHook | 相对特殊,注册函数异步并行互不影响,但影响最终回调函数的执行参数 |
1|3源码分析
PS: webpack 中的 tapable 源码不在 master 上 ,需要切到分支 tapable-1 。
从入口文件可知, 引入的 tapable (index.js) 主要就是复杂导出各种钩子。
每个钩子的结构大致如上,共同点:
- 当前钩子继承于 Hook 类。
- 通过重写
content
的实现,改变当前钩子的compile
方法。 - 同步钩子重写 tapAsync/tapPromise 阻止执行。
- 异步钩子在其原型上添加(重写覆盖)了
_call
属性。
解析一下 Hook 类的内部:
略去拦截器实现与注册的特殊配置相关代码,以简单的例子做演示跟踪。
-
创建钩子跟踪:
- 原型上定义了 _call/_promise/_callAsync ,主要作用是为了防止命名冲突。
- 执行
constructor
构造函数,初始化私有属性。
-
注册函数跟踪:
- 组织注册函数参数,增加到 taps 中(此处略去非核心功能)
-
执行注册跟踪:
- 构造函数执行时,
this.call
指向了this._call
,即为原型上的_call
,也即为createCompileDelegate('call', 'sync')
的返回值。 createCompileDelegate
通过_createCall
返回了一个新的call
。_createCall
实际是将现有的钩子内部参数及由compile
生成的函数。- 上文提到各个钩子的
compile
是经过钩子类中重写了实现的, 而在 Hook 类相当于占位符。
- 构造函数执行时,
回看 SyncHook
类中,compile
的相关实现
为了探寻 compile
的具体实现,需要解析一下 HookCodeFactory
内部实现
由此可看出 HookCodeFactory
是一个根据钩子的内部参数生成代码字符串,再及由 new Function()
的方式返回代码。
SyncHook 生成的 call 为例:
SyncWaterfallHook 生成的 call 为例:
AsyncParallelBailHook 生成的 call 为例:
所有钩子都可以通过打印 create
打印出相应的当前 hook 执行的 call 函数。
本文主要目的为讲解 tapable 在 webpack 中的机制, 各个钩子生成代码的部分不一一进行解析。
源码中关键字解释:
- _x: 注册函数的数组。
- _context : 当前注册函数的执行上下文。 (bail/waterfall 等钩子的返回值需要判断或传递给后置函数,或有拦截器等情况)
- interceptors: 拦截器, 高级用法。
- before : 在参数中的特殊标识,指 _context 或 拦截器。
- after : 在参数中的特殊标识,指钩子执行结束后的最终回调函数。
__EOF__

本文链接:https://www.cnblogs.com/qingzhao/p/16516723.html
关于博主:I am a good person
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具