前言
玩webpack
的都知道,webpack
把各個外掛串聯起來的核心是tapable
,而tapable
裡面有很多*hook
函式,其中有一個不常用,但是卻很好玩的鉤子函式叫 SyncLoopHook
, 於是我就自己實現了一遍, 然後想去和網友對比一下實現的不同。誰知道,網上很多SyncLoopHook
函式的實現都是一樣的,而且還是錯的
1. 網上普遍的SyncLoopHook
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
的回撥函式迴圈執行
如下圖, 第一個回撥函式執行了兩遍,沒問題
但是把return
放到第二個之後的就會出錯了,它只是把第二個回撥函式執行兩次
而tapable
裡的SyncLoopHook
是把包括return
非undefined
的回撥函式和該回撥函式之前的回撥函式都迴圈一遍, 如下圖
2. 我的SyncLoopHook
注意 : 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 []
}, [])
}
}
複製程式碼
有時候網上的東西也不能全信,還是要自己寫一遍才行~