Tapable(一)
Webpack本质上是一种事件流的机制,它的工作流程就是将各个插件串联起来,而实现这一切的核心就是Tapable。
Tapable 是一个小型的库,允许你对一个 javascript 模块添加和应用插件。它可以被继承或混入到其他模块中。除此之外,Tapable 还允许你通过回调函数的参数,访问事件的“触发者(emittee)”或“提供者(producer)”。
示意图(引用):
打开 Webpack 4.0 的源码中一定会看到下面这些以 Sync、Async 开头,以 Hook 结尾的方法,这些都是 tapable 核心库的类,为我们提供不同的事件流执行机制,我们称为 “钩子”。
Tapable 提供同步(Sync)和异步(Async)钩子类。而异步又分为异步串行、异步并行钩子类。
const {
SyncHook,
SyncBailHook,
SyncWaterfallHook,
SyncLoopHook,
AsyncParallelHook,
AsyncParallelBailHook,
AsyncSeriesHook,
AsyncSeriesBailHook,
AsyncSeriesWaterfallHook
} = require("tapable");
安装tapable:
yarn add tapable
同步钩子类
通过实例的 tap 方法监听函数, 通过 call发布事件
一、SyncHook:
同步串行,不关心订阅函数执行后的返回值是什么,在触发事件之后,会按照事件注册的先后顺序执行所有的事件处理函数。其原理是将监听(订阅)的函数存放到一个数组中, 发布时遍历数组中的监听函数并且将发布时的 arguments传递给监听函数
class SyncHook {
constructor(args) {
this.tasks = []; //存放监听函数的数组
}
tap(name, task) { //注册监听函数, 注册到一个数组里
this.tasks.push(task);
}
call(...args) {
// 依次执行事件处理函数
this.tasks.forEach(task => task(...args));
}
}
let hook = new SyncHook(['name']);
//tap传入两个参数:名字 (名字没有什么实际意义)、回调
hook.tap('react',function(name){
console.log('react',name) // react lzy
})
hook.tap('node',function(name){
console.log('node',name) //node lzy
})
hook.call('lzy')
二、SyncBailHook
同步串行,如果事件处理函数执行时返回值为undefined,则继续执行;否则停止向下执行(Bai保险)
class SyncBailHook {
constructor(args) {
this.tasks = [];
}
tap(name, task) {
this.tasks.push(task);
}
call(...args) {
let ret; //ret 当前函数的返回值
let index = 0; // 当前要先执行第一个
do{
ret = this.tasks[index++](...args)
}while(ret === undefined && index<this.tasks.length); // ===undefined 继续执行
}
}
let hook = new SyncBailHook(['name']);
hook.tap('vue',function(name){
console.log('vue',name) // vue lzy
})
hook.tap('react',function(name){
console.log('react',name) // react lzy
return '停止向下执行'
})
hook.tap('node',function(name){ // 不执行此处代码
console.log('node',name)
})
hook.call('lzy')
三、SyncWaterfallHook
同步串行瀑布流,瀑布流指的是第一个监听函数的返回值,做为第二个监听函数的参数。第二个函数的返回值作为第三个监听函数的参数,依次类推…
class SyncWaterfallHook {
constructor(args) {
this.tasks = [];
}
tap(name, task) {
this.tasks.push(task);
}
call(...args) {
let [first,...others] = this.tasks;
let ret = first(...args);
others.reduce((a,b)=>{
return b(a);
},ret)
}
}
let hook = new SyncWaterfallHook(['name']);
hook.tap('vue',function(name){
console.log('vue',name) // vue lzy
return 'vue-ok'
})
hook.tap('react',function(data){
console.log('react',data) // react vue-ok
return 'react-ok'
})
hook.tap('node',function(data){
console.log('node',data) // node react-ok
})
hook.call('lzy')
四、SyncLoopHook
同步串行,循环执行(如果监听函数的返回值为 true, 则反复执行当前的监听函数,直到返回值为 undefined则继续执行下面的监听函数)
class SyncLoopHook {
constructor(args) {
this.tasks = [];
}
tap(name, task) {
this.tasks.push(task);
}
call(...args) {
this.tasks.forEach(task =>{
let ret;
do{
ret = task(...args)
}while(ret !==undefined);
});
}
}
let hook = new SyncLoopHook(['name']);
let total = 0;
hook.tap('vue',function(name){
console.log('vue',name) // vue lzy(3遍)
return ++total ==3?undefined:'继续学';
})
hook.tap('react',function(name){
console.log('react',name) // react lzy
})
hook.tap('node',function(name){
console.log('node',name) //node lzy
})
hook.call('lzy')
参考文章:
https://www.jianshu.com/p/273e1c9904d2
https://www.cnblogs.com/qiqingfu/archive/2019/02/16/10387634.html
(ง •_•)ง (感谢~~)