玩webpack
的都知道,webpack
把各個插件串聯起來的核心是tapable
,而tapable
裏面有不少*hook
函數,其中有一個不經常使用,可是卻很好玩的鉤子函數叫 SyncLoopHook
, 因而我就本身實現了一遍, 而後想去和網友對比一下實現的不一樣。誰知道,網上不少SyncLoopHook
函數的實現都是同樣的,並且仍是錯的webpack
class SyncLoopHook{
constructor() {
this.tasks=[];
}
tap(name,task) {
this.tasks.push(task);
}
call(...args) {
this.tasks.forEach(task => {
let ret=true;
do {
ret = task(...args);
}while(ret)
});
}
}
複製代碼
網上大部分都是這樣實現的, 這樣寫只是把return
非undefind
的回調函數循環執行web
以下圖, 第一個回調函數執行了兩遍,沒問題bash
可是把return
放到第二個以後的就會出錯了,它只是把第二個回調函數執行兩次
而
tapable
裏的
SyncLoopHook
是把包括
return
非
undefined
的回調函數和該回調函數以前的回調函數都循環一遍, 以下圖
注意 : SyncLoopHook只能有一次循環, 若是不對, 請及時告訴我函數
class SyncLoopHook {
constructor () {
this.tasks = []
}
tap (...args) {
this.tasks.push(args.pop())
}
call (...args) {
let ret,
alreadyLoop = false // 是否已經循環了
this.tasks.reduce( (baton, task) => {
baton.push(task)
// 判斷最新一個回調函數的返回值
ret = baton[baton.length - 1](...args)
// 若是返回值爲undefined 且 若是已經循環了, 返回[], 若是還沒循環, 返回包含上一個回調的baton
if (!ret) return alreadyLoop ? [] : baton
// 若是不是undefine,遍歷baton並檢測最後一個回調函數的返回值,直到爲undefined
while (ret) ret = baton.map(e => e(...args)).pop()
// 代碼執行到這裏,證實已經循環了
alreadyLoop = true
// 已經循環了,清空baton
return []
}, [])
}
}
複製代碼
有時候網上的東西也不能全信,仍是要本身寫一遍才行~oop