Tapable 提供了哪些常见的钩子(Hook)类型?(如 SyncHook、AsyncSeriesHook 等)
Tapable 是 Webpack 的核心库,它为 Webpack 提供了强大的插件架构(基于发布-订阅模式)。Webpack 中几乎所有的核心对象(如 Compiler、Compilation)都继承或使用了 Tapable 的 Hook。
Tapable 提供了 9 种 常见的钩子(Hook)类型。为了方便理解,这些钩子可以根据 执行机制(同步/异步、串行/并行) 和 控制流逻辑(Bail、Waterfall、Loop) 进行分类。
一、 核心概念理解(命名规律)
Tapable 钩子的名称是组合而来的,理解以下关键字就能轻松掌握所有钩子:
执行时机 / 模式
Sync(同步):按顺序同步执行,注册的函数必须是同步函数。AsyncSeries(异步串行):按顺序异步执行,前一个异步任务完成后,才会执行下一个。AsyncParallel(异步并行):所有异步任务同时(并行)发起执行。
控制流逻辑(后缀)
- 基本类型(Basic):无特殊后缀。依次执行每个插件,忽略插件的返回值。
Bail(保险/熔断):当某个插件返回 非undefined的值时,立即中断后续插件的执行。Waterfall(瀑布/流水线):前一个插件的返回值会作为下一个插件的入参依次传递下去。Loop(循环):如果某个插件返回 非undefined的值,则重新从第一个插件开始循环执行,直到所有插件都返回undefined。
二、 9 种常见钩子详解
1. 同步钩子(Sync Hooks)
只能使用 tap 注册,call 触发。
SyncHook- 特点:最基础的同步钩子,按顺序执行,忽略返回值。
- 场景:纯通知类事件,如 Webpack 编译开始。
SyncBailHook- 特点:同步熔断钩子。一旦有插件返回非
undefined,立即停止执行后续插件。 - 场景:寻找符合条件的插件,一旦找到就终止。
- 特点:同步熔断钩子。一旦有插件返回非
SyncWaterfallHook- 特点:同步瀑布钩子。上一个插件的返回值是下一个插件的第一个参数。
- 场景:代码或配置的逐层修改/加工(如 Loader 链式处理)。
SyncLoopHook- 特点:同步循环钩子。只要有插件返回非
undefined,就从头重新执行所有插件。
- 特点:同步循环钩子。只要有插件返回非
2. 异步串行钩子(Async Series Hooks)
可使用 tap、tapAsync(回调)、tapPromise(Promise)注册,用 callAsync 或 promise 触发。按顺序依次执行。
AsyncSeriesHook- 特点:异步串行基本钩子。一个异步任务完成后,才执行下一个。
- 场景:有先后顺序依赖的异步操作,如构建资源打包文件。
AsyncSeriesBailHook- 特点:异步串行熔断钩子。某个异步任务返回非
undefined(或 promise resolve 非 undefined),阻断后续任务。
- 特点:异步串行熔断钩子。某个异步任务返回非
AsyncSeriesWaterfallHook- 特点:异步串行瀑布钩子。异步流式传递数据,上一个异步结果传给下一个。
- 场景:异步获取数据并逐层加工处理。
AsyncSeriesLoopHook- 特点:异步串行循环钩子。如果异步任务返回非
undefined,重新开始循环。
- 特点:异步串行循环钩子。如果异步任务返回非
3. 异步并行钩子(Async Parallel Hooks)
同样支持 tapAsync / tapPromise。所有注册的异步任务同时触发。
AsyncParallelHook- 特点:异步并行基本钩子。类似于
Promise.all,所有异步任务同时发起,等全部完成后再继续。 - 场景:互不干扰的异步操作,如多进程编译、同时分析多个无依赖的资源。
- 特点:异步并行基本钩子。类似于
AsyncParallelBailHook- 特点:异步并行熔断钩子。虽然是并行执行,但只要有一个任务返回了有效值,就会提前触发最终的回调(相当于
Promise.race的某种变体)。
- 特点:异步并行熔断钩子。虽然是并行执行,但只要有一个任务返回了有效值,就会提前触发最终的回调(相当于
三、 一张图总结 Tapable 钩子矩阵
| 逻辑类型 \ 执行模式 | Sync (同步) | AsyncSeries (异步串行) | AsyncParallel (异步并行) |
|---|---|---|---|
| Basic (基本) | SyncHook |
AsyncSeriesHook |
AsyncParallelHook |
| Bail (熔断) | SyncBailHook |
AsyncSeriesBailHook |
AsyncParallelBailHook |
| Waterfall (瀑布) | SyncWaterfallHook |
AsyncSeriesWaterfallHook |
❌ (并行无法实现数据流传递) |
| Loop (循环) | SyncLoopHook |
AsyncSeriesLoopHook |
❌ (并行循环无实际逻辑意义) |
四、 补充:注册与触发方式
使用 Tapable 时,插件的监听(订阅)与触发(发布)方式如下:
| Hook 类型 | 注册方式 (Subscribe) | 触发方式 (Publish) |
|---|---|---|
| Sync | tap |
call(...) |
| Async | tap (同步模拟)tapAsync (cb 回调)tapPromise (返回 Promise) |
callAsync(..., done)promise(...) |
简单代码示例(以 AsyncSeriesHook 为例):
javascript
const { AsyncSeriesHook } = require('tapable');
// 1. 实例化 hook,指定接收的参数
const hook = new AsyncSeriesHook(['name']);
// 2. 注册插件 (Promise 方式)
hook.tapPromise('PluginA', (name) => {
return new Promise((resolve) => {
setTimeout(() => {
console.log(`PluginA: ${name}`);
resolve();
}, 1000);
});
});
// 3. 触发 hook
hook.promise('Webpack').then(() => {
console.log('所有异步串行任务完成!');
});
右滑查看面试常问