webpack有两个非常重要的类:Compiler和Compilation。他们通过注入插件的方式,来监听webpack的所有生命周期,插件的注入离不开各种各样的Hook,而他们的Hook是如何得到的呢?其实是创建了Tapable库中各种Hook的实例。
Tapable是一个任务调度库,它的核心思基于发布订阅模式是将任务执行逻辑和调度逻辑分离,Tapable在webpack中用于plugin的管理,在可以实现复杂调度逻辑的同时尽可能保证可维护性。
Tapable的机制与Event类似,它可以用来定义各种各样的钩子,相当于Event中的事件注册,但是与Event不同的是,Event中各个事件之间相互不关联,互不影响,但是tapable注册的事件之间可以是有关系的,这种关系通过Tapable定义的各个钩子类来实现。其实,Tapable的核心就是这些钩子类。因此,我们接下来的重点就是讲述这些钩子类,并实现它们。
Tapable的每个子类都是一个用于注册和触发事件的钩子,我们可以查看一下SyncHook实例身上有哪些属性,找到它注册事件和触发事件的属性。
SyncHook {
_args: [],
name: undefined,
taps: [],
interceptors: [],
_call: [Function: CALL_DELEGATE],
call: [Function: CALL_DELEGATE], // 用于触发同步事件的钩子
_callAsync: [Function: CALL_ASYNC_DELEGATE],
callAsync: [Function: CALL_ASYNC_DELEGATE], // 用于触发异步事件的钩子
_promise: [Function: PROMISE_DELEGATE],
promise: [Function: PROMISE_DELEGATE],
_x: undefined,
compile: [Function: COMPILE],
tap: [Function: tap], // 用于注册同步事件的钩子
tapAsync: [Function: TAP_ASYNC], // 用于注册异步事件的钩子
tapPromise: [Function: TAP_PROMISE],
constructor: [Function: SyncHook]
}
const { SyncHook } = require("tapable");
class myCompiler {
constructor() {
this.hooks = {
// 1. 创建hooks(webpack完成)
syncHook: new SyncHook(["name", "age"]),
};
// 2. 用hooks监听事件(自定义plugin)
this.hooks.syncHook.tap("event1", (name, age) => {
console.log("event1事件监听执行了:", name, age);
});
this.hooks.syncHook.tap("event2", (name, age) => {
console.log("event2事件监听执行了:", name, age);
});
}
}
const compiler = new myCompiler();
// 3. 发出去事件(webpack完成)
compiler.hooks.syncHook.call("小张", 20);
结果:
当有返回值时,就不会执行后续的事件触发了
const { SyncBailHook } = require("tapable");
class myCompiler {
constructor() {
this.hooks = {
// 1. 创建hooks
bailHook: new SyncBailHook(["name", "age"]),
};
// 2. 用hooks监听事件(自定义plugin)
this.hooks.bailHook.tap("event1", (name, age) => {
console.log("event1事件监听执行了:", name, age);
return 123
});
this.hooks.bailHook.tap("event2", (name, age) => {
console.log("event2事件监听执行了:", name, age);
});
}
}
const compiler = new myCompiler();
// 3. 发出去事件
compiler.hooks.bailHook.call("小张", 20);
输出结果:
当返回值为true,就会反复执行该事件,当返回值为undefined或者不返回内容,就退出事件
const { SyncLoopHook } = require("tapable");
class myCompiler {
constructor() {
this.hooks = {
// 1. 创建hooks
loopHook: new SyncLoopHook(["name", "age"]),
};
let count = 0;
// 2. 用hooks监听事件(自定义plugin)
this.hooks.loopHook.tap("event1", (name, age) => {
if (count < 5) {
console.log("event1事件监听执行了:", name, age);
count++;
return true;
} else {
return;
}
});
this.hooks.loopHook.tap("event2", (name, age) => {
console.log("event2事件监听执行了:", name, age);
});
}
}
const compiler = new myCompiler();
// 3. 发出去事件
compiler.hooks.loopHook.call("小张", 20);
输出结果:
当返回值不为undefined时,会将这次返回的结果作为下次事件的第一个参数
const { SyncWaterfallHook } = require("tapable");
class myCompiler {
constructor() {
this.hooks = {
// 1. 创建hooks
waterfallHook: new SyncWaterfallHook(["name", "age"]),
};
// 2. 用hooks监听事件(自定义plugin)
this.hooks.waterfallHook.tap("event1", (name, age) => {
console.log("event1事件监听执行了:", name, age);
// return “小李”,小李作为下一个事件的第一个参数
return "小李";
});
this.hooks.waterfallHook.tap("event2", (name, age) => {
console.log("event2事件监听执行了:", name, age);
});
}
}
const compiler = new myCompiler();
// 3. 发出去事件
compiler.hooks.waterfallHook.call("小张", 20);
输出结果:
并行,不会等到上一个事件回调执行结束,才会执行下一次事件处理回调
const { AsyncParallelHook } = require("tapable");
class myCompiler {
constructor() {
this.hooks = {
// 1. 创建hooks
parallelHook: new AsyncParallelHook(["name", "age"]),
};
// 2. 用hooks监听事件(自定义plugin)
this.hooks.parallelHook.tapAsync("event1", (name, age) => {
setTimeout(() => {
console.log("event1事件监听执行了:", name, age);
}, 3000);
});
this.hooks.parallelHook.tapAsync("event2", (name, age) => {
setTimeout(() => {
console.log("event2事件监听执行了:", name, age);
}, 3000);
});
}
}
const compiler = new myCompiler();
// 3. 发出去事件
compiler.hooks.parallelHook.callAsync("小张", 20);
输出结果:
串行,会等待上一次异步的Hook,即按照注册的顺序依次执行
const { AsyncSeriesHook } = require("tapable");
class myCompiler {
constructor() {
this.hooks = {
// 1. 创建hooks
seriesHook: new AsyncSeriesHook(["name", "age"]),
};
// 2. 用hooks监听事件(自定义plugin)
this.hooks.seriesHook.tapAsync("event1", (name, age, callback) => {
console.log("event1事件监听执行了:", name, age);
// 这个callback决定下一个事件的执行
callback();
});
this.hooks.seriesHook.tapAsync("event2", (name, age, callback) => {
console.log("event2事件监听执行了:", name, age);
// 这个callback决定发出事件中的函数执行
callback();
});
}
}
const compiler = new myCompiler();
// 3. 发出去事件
// 第三个参数决定第一个事件的执行
compiler.hooks.seriesHook.callAsync("小张", 20, () => {
console.log("所有事件都执行完成啦!");
});
输出结果: