tapable

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");
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)
  • 異步鉤子(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)

下面將分別引入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ù)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蜗细,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子怒详,更是在濱河造成了極大的恐慌炉媒,老刑警劉巖踪区,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異橱野,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)善玫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門水援,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人茅郎,你說我怎么就攤上這事蜗元。” “怎么了系冗?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵奕扣,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我掌敬,道長(zhǎng)惯豆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任奔害,我火速辦了婚禮楷兽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘华临。我一直安慰自己芯杀,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布雅潭。 她就那樣靜靜地躺著揭厚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪扶供。 梳的紋絲不亂的頭發(fā)上筛圆,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音椿浓,去河邊找鬼顽染。 笑死,一個(gè)胖子當(dāng)著我的面吹牛轰绵,可吹牛的內(nèi)容都是我干的粉寞。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼左腔,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼唧垦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起液样,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤振亮,失蹤者是張志新(化名)和其女友劉穎巧还,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體坊秸,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡麸祷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了褒搔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片阶牍。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖星瘾,靈堂內(nèi)的尸體忽然破棺而出走孽,到底是詐尸還是另有隱情,我是刑警寧澤琳状,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布磕瓷,位于F島的核電站,受9級(jí)特大地震影響念逞,放射性物質(zhì)發(fā)生泄漏困食。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧摩泪,春花似錦糙及、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至仰剿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間痴晦,已是汗流浹背南吮。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留誊酌,地道東北人部凑。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像碧浊,于是被迫代替她去往敵國(guó)和親涂邀。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容