tapable同步方法
上一节学习了tapable的一个同步钩子,SyncHook,这节来学习另外几个同步的钩子,它们各有各的特点。
SyncHook
上一节学的钩子,先注册任务到一个数组中,然后按顺序依次执行。
SyncBailHook
同步保险钩子。写同步的时候增加一个保险,这个保险一般称之为熔断性,当我们前一个事件执行完成后,可以决定是否继续向下执行。
增加一个保险。
引用tapable中的SyncBailHook
当其中一个任务返回非undefined的值时,就不会继续向下执行了
// webpack\webpack-tapable\2.start.js
// 同步保险钩子 SyncBailHook tapable
// 当其中一个任务返回非undefined的值时,就不会继续向下执行了
let { SyncBailHook } = require('tapable')
class Lesson {
// new 对象时 构造函数会被执行
constructor() {
// new对象的时候 注册一些钩子
// 一旦new一个新的实例时,这些钩子就被启用了
this.hook = {
// 假如有个架构课
arch: new SyncBailHook(['name']), // 可能会传一个参数,参数是可选的
}
}
// 用来注册的方法
tap() { // 注册监听函数
this.hook.arch.tap('vue', function (name) {
console.log('vue 111', name);
return '学不下去了,停止学习'
// SyncBailHook 同步保险钩子,当有一个任务返回一个非undefined的值,就不会继续向下执行了
}) // 拿到钩子实例,实例上就有一个tap方法
this.hook.arch.tap('react', function (name) {
console.log('react 111', name);
})
// 001 调用这个方法时,会将上面两个事件注册到数组中
}
// 启动钩子的方法
start() {
// 执行之前注册的事件
this.hook.arch.call('cheny') // 实例上有一个call 方法,执行注册的事件
// 002 调用这个方法时,会让数组中注册的方法依次执行
}
}
// 测试
let l = new Lesson() // 声明一个实例
// 注册两个事件
l.tap()
// 调用start方法 启动钩子
l.start()
打印输出结果,发现符合我们的期望,学习完vue之后,就不会继续向下学习react了
假如返回undefined,就会继续向下执行了。
自己实现SyncBailHook
当注册的任务有一个返回非undefined的值时,就不继续向下执行
// webpack\webpack-tapable\2.case.js
// 当注册的任务有一个返回非undefined的值时,就不继续向下执行
// 相当于增加了一个保险
class SyncBailHook { // 同步保险钩子
constructor(args) { // args -> ['name']
this.tasks = [] // 保存注册的事件
}
// 同步注册事件
tap(name, task) {
this.tasks.push(task)
}
// 按顺序依次执行注册过的事件
// 如果其中一个返回非undefined的值后,就不继续向下执行了
call(...args) {
let ret; // 当前这个函数的返回值
let index = 0; // 当前执行到第几个函数了,如果超出范围,说明执行完毕,结束循环
// 使用do while 至少执行一次
do {
ret = this.tasks[index](...args)
index++
} while (ret === undefined && index < this.tasks.length);
}
}
let hook = new SyncBailHook(['name']) // 参数可以传可以不传
// 注册事件
hook.tap('vue', function (name) {
console.log('vue 1111', name);
// 假设学习完这个就不想再继续往下学了
// 那么就让这个任务返回非undefined的值
return '就学到这吧,不继续向下学了'
})
hook.tap('react', function (name) {
console.log('react 1111', name);
})
// 依次执行
hook.call('cheny')
测试一下,是我们想要的结果,当学习完vue就不想继续往下学了。
SyncWaterfallHook
同步瀑布钩子。上一个任务的返回值,会被下一个任务接收到。就像一个瀑布一样,可以知道上一个任务的执行结果。
就是假如学习完vue之后,返回一个学习状态,然后再学习react时,我们知道vue学的好不好。
这就是同步瀑布钩子的应用场景。
其实就是一个流程控制。
引用tapable中的SyncWaterfallHook
当前任务的返回结果会被下一个任务接收到。
// webpack\webpack-tapable\3.start.js
// 同步瀑布钩子 SyncWaterfallHook tapable
// 当前任务的返回值,会被下一个任务接收到
let { SyncWaterfallHook } = require('tapable')
class Lesson {
// new 对象时 构造函数会被执行
constructor() {
// new对象的时候 注册一些钩子
// 一旦new一个新的实例时,这些钩子就被启用了
this.hook = {
// 假如有个架构课
arch: new SyncWaterfallHook(['name']), // 可能会传一个参数,参数是可选的
}
}
// 用来注册的方法
tap() { // 注册监听函数
this.hook.arch.tap('vue', function (name) {
console.log('vue 111', name);
return 'vue 学的还不错'
// SyncWaterfallHook 同步瀑布钩子
// 当前任务的返回值,会被下一个任务接收到
}) // 拿到钩子实例,实例上就有一个tap方法
this.hook.arch.tap('react', function (data) {
// data就会接收到上一个任务的返回结构
console.log('react 111', data);
})
// 001 调用这个方法时,会将上面两个事件注册到数组中
}
// 启动钩子的方法
start() {
// 执行之前注册的事件
this.hook.arch.call('cheny') // 实例上有一个call 方法,执行注册的事件
// 002 调用这个方法时,会让数组中注册的方法依次执行
}
}
// 测试
let l = new Lesson() // 声明一个实例
// 注册两个事件
l.tap()
// 调用start方法 启动钩子
l.start()
查看输出结构,vue学完之后的状态被react接收到了,是我们想要的结果。
自己实现SyncWaterfallHook
上一个函数的结果,是这个函数的参数
// webpack\webpack-tapable\3.case.js
// 当前任务的返回结果,会被下一个任务接收到
class SyncWaterfallHook { // 同步瀑布钩子
constructor(args) { // args -> ['name']
this.tasks = [] // 保存注册的事件
}
// 同步注册事件
tap(name, task) {
this.tasks.push(task)
}
// 按顺序依次执行注册过的事件
// 当前任务的返回结果,会被下一个任务接收到
call(...args) {
let [first, ...others] = this.tasks
let ret = first(...args)
// 上一个函数的结果,是下一个函数的参数
others.reduce((pre, cur) => {
return cur(pre)
}, ret)
}
}
let hook = new SyncWaterfallHook(['name']) // 参数可以传可以不传
// 注册事件
hook.tap('vue', function (name) {
console.log('vue 1111', name);
// 当前任务的返回结果,会被下个任务接收到
return 'vue学的还不错'
})
hook.tap('react', function (data) {
console.log('react 1111', data);
// 当前任务的返回结果,会被下个任务接收到
return 'react学的一般'
})
hook.tap('webpack', function (data) {
console.log('webpack 1111', data);
})
// 依次执行
hook.call('cheny')
我们看一下结果,是我们想要的
SyncLoopHook
同步循环钩子。这个比较怪异,可以让某个任务执行多次之后,再去执行下一个。
webpack中并没有用到这个方法。
引用tapable中的SyncLoopHook
碰到某个任务时,循环执行。
当任务返回undefined时,放行,不返回undefined就一直执行。
所以可以使用一个count计数器来控制执行次数。
// webpack\webpack-tapable\4.start.js
// 同步循环钩子 SyncLoopHook tapable
// 某个任务执行多次后,再执行下一个
// 遇到某个不返回undefined的监听函数,会多次执行
let { SyncLoopHook } = require('tapable')
class Lesson {
// new 对象时 构造函数会被执行
constructor() {
// new对象的时候 注册一些钩子
// 一旦new一个新的实例时,这些钩子就被启用了
this.hook = {
// 假如有个架构课
arch: new SyncLoopHook(['name']), // 可能会传一个参数,参数是可选的
}
this.count = 0 // 需要循环执行任务的执行次数
}
// 用来注册的方法
tap() { // 注册监听函数
this.hook.arch.tap('vue', (name) => {
console.log('vue 111', name);
return ++this.count === 3 ? undefined : '继续学'
// SyncLoopHook 同步循环钩子
// 当前任务执行多次后,再去执行下一个
// 不返回undefined 就一直执行
// count变为3后,再让它返回undefined,放行
}) // 拿到钩子实例,实例上就有一个tap方法
this.hook.arch.tap('react', (name) => {
console.log('react 111', name);
})
// 001 调用这个方法时,会将上面两个事件注册到数组中
}
// 启动钩子的方法
start() {
// 执行之前注册的事件
this.hook.arch.call('cheny') // 实例上有一个call 方法,执行注册的事件
// 002 调用这个方法时,会让数组中注册的方法依次执行
}
}
// 测试
let l = new Lesson() // 声明一个实例
// 注册两个事件
l.tap()
// 调用start方法 启动钩子
l.start()
看一下输出结果,我们希望vue学3遍之后,再放行,使我们想要的结果。
自己实现SyncLoopHook
// webpack\webpack-tapable\4.case.js
// 当前任务如果不返回undefined,就会一直执行
// 只有当返回undefined时,才会放行
// 可以使用一个计数器count,控制任务执行次数
// 当执行次数为期望值时,让函数返回undefined,放行
class SyncLoopHook { // 同步循环钩子
constructor(args) { // args -> ['name']
this.tasks = [] // 保存注册的事件
}
// 同步注册事件
tap(name, task) {
this.tasks.push(task)
}
// 按顺序依次执行注册过的事件
// 某个任务不返回undefined时,就一直执行
call(...args) {
// 首先 所有的任务都要执行一遍
this.tasks.forEach(task => {
// 如果某个任务不返回undefined 就一直执行
let ret; // 当前任务的返回值
do {
ret = task(...args)
} while (ret !== undefined);
})
}
}
let hook = new SyncLoopHook(['name']) // 参数可以传可以不传
// 注册事件
let count = 0; // 计数器,控制任务执行次数
hook.tap('vue', function (name) {
console.log('vue 1111', name);
// 需要学习3遍,每次执行count加1,当count为3时,让函数返回undefined,放行
return ++count === 3 ? undefined : '继续学'
})
hook.tap('react', function (name) {
console.log('react 1111', name);
})
hook.tap('webpack', function (name) {
console.log('webpack 1111', name);
})
// 依次执行
hook.call('cheny')
看一下结果,希望vue学习3遍后,在执行下面的任务,输出结果符合期望
总结
webpack本质上是一种事件流的机制,它的工作流程就是将各个插件串联起来。
而实现这一切的核心就是Tapable。
Tapable有点类似于nodejs的events库,核心原理也是依赖发布订阅模式。
其中一共有四个同步钩子,分别有不同的执行效果。
SyncHook
最基础的同步钩子,按顺序将任务注册到数组中,然后再依次执行。
jscall(...args) { this.tasks.forEach((task) => { task(...args) }) }
SyncBailHook
同步保险钩子。增加一道保险,按顺序执行任务时,会对返回值做判断,如果返回值不为undefined,就不继续向下执行了。
js// 按顺序依次执行注册过的事件 // 如果其中一个返回非undefined的值后,就不继续向下执行了 call(...args) { let ret; // 当前这个函数的返回值 let index = 0; // 当前执行到第几个函数了,如果超出范围,说明执行完毕,结束循环 // 使用do while 至少执行一次 do { ret = this.tasks[index](...args) index++ } while (ret === undefined && index < this.tasks.length); }
SyncWaterfallHook
同步瀑布钩子。依次执行注册任务,当前任务的返回值,会作为下一个任务的参数。相当于把每个任务的执行状态依次传递了下去。
js// 按顺序依次执行注册过的事件 // 当前任务的返回结果,会被下一个任务接收到 call(...args) { let [first, ...others] = this.tasks let ret = first(...args) // 上一个函数的结果,是下一个函数的参数 others.reduce((pre, cur) => { return cur(pre) }, ret) }
SyncLoopHook
同步循环钩子。webpack中并没有使用这个钩子,不过也挺有意思的。
依次执行注册任务,当某个任务返回值不为undefined时,就一直循环执行该任务。
js// 按顺序依次执行注册过的事件 // 某个任务不返回undefined时,就一直执行 call(...args) { // 首先 所有的任务都要执行一遍 this.tasks.forEach(task => { // 如果某个任务不返回undefined 就一直执行 let ret; // 当前任务的返回值 do { ret = task(...args) } while (ret !== undefined); }) }
所以使用的时候,可以用个计数器,控制执行次数。
js// 注册事件 let count = 0; // 计数器,控制任务执行次数 hook.tap('vue', function (name) { console.log('vue 1111', name); // 需要学习3遍,每次执行count加1,当count为3时,让函数返回undefined,放行 return ++count === 3 ? undefined : '继续学' })
参考
https://www.bilibili.com/video/BV1a4411e7Bz?p=29&spm_id_from=pageDriver