webpack的插件本質(zhì)上是基于事件流的機(jī)制,它的工作流程就是將各個(gè)插件串連起來蛮穿,而實(shí)現(xiàn)這一切的核心就是Tabable。Tabable有點(diǎn)類似于nodejs的EventEmitter庫(kù),核心原理也是依賴發(fā)布訂閱模式垒手。
Tapable
Tabable提供了很多鉤子類,可大致分為同步(sync*)和異步(async*)兩種
const {
SyncHook,
SyncBailHook,
SyncWaterfallHook,
SyncLoopHook,
AsyncParallelHook,
AsyncParallelBailHook,
AsyncSeriesHook,
AsyncSeriesBailHook,
AsyncSeriesWaterfallHook
} = require("tapable");
安裝
yarn add tapable
見個(gè)面
所有的鉤子構(gòu)造函數(shù)都接受一個(gè)可選的參數(shù)(這個(gè)參數(shù)最好是數(shù)組倒信,不是數(shù)組tapable內(nèi)部也把他變成數(shù)組)科贬,這是一個(gè)參數(shù)的字符串名字列表。下面以簡(jiǎn)單的同步鉤子為例鳖悠,帶大家先體驗(yàn)一下tapable的基本使用
let { SyncHook } = require('Tapable')
let hook = new SyncHook(['name']) //通過SyncHook實(shí)例化一個(gè)同步鉤子唆迁,參數(shù)可選,最好傳數(shù)組
hook.tap('hello',function(name){ //注冊(cè)函數(shù)
console.log('hello ', name)
})
hook.tap('welcome',function(name){ //注冊(cè)函數(shù)
console.log('welcome', name)
})
hook.call('word') //觸發(fā)函數(shù)
//hello word
//welcome word
在案例中竞穷,我們通過SyncHook
構(gòu)建了一個(gè)同步鉤子唐责,然后調(diào)用tap
注冊(cè)了兩個(gè)函數(shù),再調(diào)用call
依次觸發(fā)注冊(cè)的回調(diào)函數(shù)瘾带。
tap
用于綁定同步鉤子的API鼠哥,綁定異步鉤子需要使用tapAsync
(綁定異步鉤子的API)和tapPromise
(綁定promise鉤子的API)
使用與實(shí)現(xiàn)
下面將介紹以下幾個(gè)同步和異步鉤子的使用,再簡(jiǎn)單的實(shí)現(xiàn)一下
特點(diǎn)概覽
- 同步鉤子(sync*)
- SyncHook:
同步串行看政,不關(guān)心訂閱函數(shù)執(zhí)行后的返回值是什么朴恳,在觸發(fā)事件之后,會(huì)按照事件注冊(cè)的先后順序執(zhí)行所有的事件處理函數(shù)允蚣。
- SyncBailHook:
同步串行于颖,執(zhí)行過程中注冊(cè)的回調(diào)返回非 undefined 時(shí)就停止不在執(zhí)行(Bai保險(xiǎn))
- SyncWaterfallHook:
同步串行瀑布流,上一個(gè)注冊(cè)的回調(diào)返回值會(huì)作為下一個(gè)注冊(cè)的回調(diào)的參數(shù)
- SyncLoopHook:
同步串行嚷兔,在執(zhí)行過程中回調(diào)返回非 undefined 時(shí)繼續(xù)再次執(zhí)行當(dāng)前的回調(diào)
- SyncHook:
- 異步鉤子(async*)
- AsyncParallelHook:
異步并行森渐,當(dāng)注冊(cè)的所有異步回調(diào)都并行執(zhí)行完畢之后再執(zhí)行 callAsync 或者 promise 中的函數(shù)
- AsyncSeriesHook:
異步串行做入,順序的執(zhí)行異步函數(shù)
- AsyncParallelBailHook:
異步并行,執(zhí)行過程中注冊(cè)的回調(diào)返回非 undefined 時(shí)就會(huì)直接執(zhí)行 callAsync 或者 promise 中的函數(shù)(由于并行執(zhí)行的原因同衣,注冊(cè)的其他回調(diào)依然會(huì)執(zhí)行)
- AsyncSeriesBailHook:
異步串行竟块,執(zhí)行過程中注冊(cè)的回調(diào)返回非 undefined 時(shí)就會(huì)直接執(zhí)行 callAsync 或者 promise 中的函數(shù),并且注冊(cè)的后續(xù)回調(diào)都不會(huì)執(zhí)行
- AsyncSeriesWaterfallHook:
異步串行瀑布流耐齐,與 SyncWaterfallHook 類似浪秘,上一個(gè)注冊(cè)的異步回調(diào)執(zhí)行之后的返回值會(huì)傳遞給下一個(gè)注冊(cè)的回調(diào)
- AsyncParallelHook:
下面將分別引入Tapable庫(kù)的這幾個(gè)同步和異步鉤子,查看執(zhí)行結(jié)果埠况,并著手實(shí)現(xiàn)一下相同的效果
同步鉤子
SyncHook
- 特點(diǎn):同步串行耸携,不關(guān)心訂閱函數(shù)執(zhí)行后的返回值是什么,在觸發(fā)事件之后辕翰,會(huì)按照事件注冊(cè)的先后順序執(zhí)行所有的事件處理函數(shù)违帆。其原理是將監(jiān)聽(訂閱)的函數(shù)存放到一個(gè)數(shù)組中, 發(fā)布時(shí)遍歷數(shù)組中的監(jiān)聽函數(shù)并且將發(fā)布時(shí)的 arguments傳遞給監(jiān)聽函數(shù)
- 使用:(請(qǐng)參考前面[見個(gè)面])
- 實(shí)現(xiàn):
class SyncHook {
constructor(args){
//記錄添加的hook(回調(diào)函數(shù))
this.hooks = []
}
tap(name,hook){
//注冊(cè)回調(diào)函數(shù)
this.hooks.push(hook)
}
call(...args){
//依次執(zhí)行注冊(cè)的回調(diào)函數(shù)
this.hooks.forEach(hook=>hook(...args))
}
}
// 使用
let hook = new SyncHook(['name']) //通過SyncHook實(shí)例化一個(gè)同步鉤子,參數(shù)可選金蜀,最好傳數(shù)組
hook.tap('hello',function(name){ //注冊(cè)函數(shù)
console.log('hello ', name)
})
hook.tap('welcome',function(name){ //注冊(cè)函數(shù)
console.log('welcome', name)
})
hook.call('word')
//hello word
//welcome word
SyncBailHook
- 特點(diǎn):同步串行刷后,執(zhí)行過程中注冊(cè)的回調(diào)返回非 undefined 時(shí)就停止不在執(zhí)行(Bai保險(xiǎn))
- 使用:
let { SyncBailHook } = require('Tapable')
let hook = new SyncBailHook(['name'])
hook.tap('hello',function(name){
console.log('hello ', name)
return '好久不見!' // 返回值不為undefined時(shí)停止執(zhí)行后面函數(shù)
})
hook.tap('welcome',function(name){
console.log('welcome', name)
})
hook.call('word')
//hello word
- 實(shí)現(xiàn)
class SyncBailHook{
constructor(args){
this.hooks = []
}
tap(name,hook){
this.hooks.push(hook)
}
call(...args){
if(this.hooks.length === 0){
return
}
let ret; //當(dāng)前函數(shù)返回值
let index = 0; //從第一個(gè)還是開始執(zhí)行
do {
ret = this.hooks[index++](...args)
} while (ret === undefined && index < this.hooks.length);
}
}
let hook = new SyncBailHook(['name'])
hook.tap('hello',function(name){
console.log('hello ', name)
return ''好久不見渊抄!' // 返回值不為undefined時(shí)停止執(zhí)行后面函數(shù)
})
hook.tap('welcome',function(name){
console.log('welcome', name)
})
hook.call('word')
//hello word
SyncWaterfallHook
- 特點(diǎn):同步串行瀑布流尝胆,上一個(gè)注冊(cè)的回調(diào)返回值會(huì)作為下一個(gè)注冊(cè)的回調(diào)的參數(shù)
- 使用:
let { SyncWaterfallHook } = require('Tapable')
let hook = new SyncWaterfallHook(['name'])
hook.tap('hello',function(name){
console.log('hello ', name)
return '好久不見!' // 返回值將作為后面的參數(shù)
})
hook.tap('welcome',function(name){
console.log('welcome', name) // 沒用返回值护桦,將返回參數(shù)
})
hook.tap('again',function(name){
console.log('again', name)
})
hook.call('word')
//hello word
//welcome 好久不見含衔!
//again 好久不見!
- 實(shí)現(xiàn)
class SyncWaterfallHook{
constructor(args){
this.hooks = []
}
tap(name,hook){
this.hooks.push(hook)
}
call(...args){
if(this.hooks.length === 0){
return
}
let [first,...other] = this.hooks
let ret = first(...args)
other.reduce((a,b)=>{
let rest = b(a)
if(rest !== undefined){
return rest
}
return a
}, ret)
}
}
let hook = new SyncWaterfallHook(['name'])
hook.tap('hello',function(name){
console.log('hello ', name)
return '好久不見二庵!' // 返回值將作為后面的參數(shù)
})
hook.tap('welcome',function(name){
console.log('welcome', name) // 沒用返回值贪染,將返回參數(shù)
})
hook.tap('again',function(name){
console.log('again', name)
})
hook.call('word')
//hello word
//welcome 好久不見!
//again 好久不見催享!
SyncLoopHook
- 特點(diǎn):同步串行杭隙,在執(zhí)行過程中回調(diào)返回非 undefined 時(shí)繼續(xù)再次執(zhí)行當(dāng)前的回調(diào)
- 使用:
let { SyncLoopHook } = require('Tapable')
let hook = new SyncLoopHook(['name'])
let index = 0
hook.tap('hello',function(name){
console.log('hello ', name)
return ++index === 5 ? undefined : 'again'
})
hook.tap('welcome',function(name){
console.log('welcome', name)
})
hook.call('word')
//hello word
//hello word
//hello word
//hello word
//hello word
//welcome word
- 實(shí)現(xiàn):
class SyncLoopHook{
constructor(args){
this.hooks = []
}
tap(name,hook){
this.hooks.push(hook)
}
call(...args){
this.hooks.forEach(hook=>{
let ret;
do {
ret = hook(...args)
} while (ret !== undefined);
})
}
}
let hook = new SyncLoopHook(['name'])
let index = 0
hook.tap('hello',function(name){
console.log('hello ', name)
return ++index === 5 ? undefined : 'again'
})
hook.tap('welcome',function(name){
console.log('welcome', name)
})
hook.call('word')
//hello word
//hello word
//hello word
//hello word
//hello word
//welcome word
異步鉤子
AsyncParallelHook
- 特點(diǎn):異步并行,當(dāng)注冊(cè)的所有異步回調(diào)都并行執(zhí)行完畢之后再執(zhí)行 callAsync 或者 promise 中的函數(shù)
- 使用:
// 后續(xù)
- 實(shí)現(xiàn):
// 后續(xù)
AsyncSeriesHook
- 特點(diǎn):異步串行因妙,順序的執(zhí)行異步函數(shù)
- 使用:
// 后續(xù)
- 實(shí)現(xiàn):
// 后續(xù)
AsyncParallelBailHook
- 特點(diǎn):異步并行痰憎,執(zhí)行過程中注冊(cè)的回調(diào)返回非 undefined 時(shí)就會(huì)直接執(zhí)行 callAsync 或者 promise 中的函數(shù)(由于并行執(zhí)行的原因,注冊(cè)的其他回調(diào)依然會(huì)執(zhí)行)
- 使用:
// 后續(xù)
- 實(shí)現(xiàn):
// 后續(xù)
AsyncSeriesBailHook
- 特點(diǎn):異步串行攀涵,執(zhí)行過程中注冊(cè)的回調(diào)返回非 undefined 時(shí)就會(huì)直接執(zhí)行 callAsync 或者 promise 中的函數(shù)铣耘,并且注冊(cè)的后續(xù)回調(diào)都不會(huì)執(zhí)行
- 使用:
// 后續(xù)
- 實(shí)現(xiàn):
// 后續(xù)
AsyncSeriesWaterfallHook
- 特點(diǎn):異步串行瀑布流,與 SyncWaterfallHook 類似以故,上一個(gè)注冊(cè)的異步回調(diào)執(zhí)行之后的返回值會(huì)傳遞給下一個(gè)注冊(cè)的回調(diào)
- 使用:
// 后續(xù)
- 實(shí)現(xiàn):
// 后續(xù)