tapable是一種事件驅動型事件流機制,本身是一個獨立的庫纷铣。webpack 通過 tapable 將實現(xiàn)與流程解耦召烂,所有具體實現(xiàn)通過插件的形式存在。
一. 工作流程
1.實例化Hook注冊事件監(jiān)聽
2.通過Hook觸發(fā)事件監(jiān)聽
3.執(zhí)行懶編譯生成的可執(zhí)行代碼
二庵寞、 Hook
Hook的執(zhí)行機制可分為同步和異步兩種,其中異步又分為并行和串行兩種模式薛匪。
Hook的鉤子類型分為四種:
1. Hook:普通鉤子捐川,監(jiān)聽器之間互相獨立不干擾
2. BailHook:熔斷鉤子,某個監(jiān)聽返回非undefined時后續(xù)不執(zhí)行
3. WaterfallHook:瀑布鉤子蛋辈,上一個監(jiān)聽的返回值可傳遞至下一個
4. LoopHook:循環(huán)鉤子属拾,如果當前返回非undefined則一直執(zhí)行(從頭開始執(zhí)行)(webpack中不常見)
三、同步鉤子
- SyncHook
每個事件獨立執(zhí)行冷溶,互相不影響
const { SyncHook } = require('tapable')
let hook = new SyncHook(['name', 'age'])
hook.tap('fn1', function (name, age) {
console.log('fn1--->', name, age)
})
hook.tap('fn2', function (name, age) {
console.log('fn2--->', name, age)
})
hook.call('jerry', 18)
// fn1---> jerry 18
// fn2---> jerry 18
- SyncBailHook
某個監(jiān)聽返回非undefined時后續(xù)不執(zhí)行
const { SyncBailHook } = require('tapable')
let hook = new SyncBailHook(['name', 'age'])
hook.tap('fn1', function (name, age) {
console.log('fn1--->', name, age)
})
hook.tap('fn2', function (name, age) {
console.log('fn2--->', name, age)
return undefined
})
hook.tap('fn3', function (name, age) {
console.log('fn3--->', name, age)
})
hook.call('jerry', 100)
// fn1---> jerry 100
// fn2---> jerry 100
// fn3---> jerry 100
- SyncWaterfallHook
上一個監(jiān)聽的返回值可傳遞至下一個渐白,覆蓋原來的參數(shù)值
const { SyncWaterfallHook } = require('tapable')
let hook = new SyncWaterfallHook(['name', 'age'])
hook.tap('fn1', function (name, age) {
console.log('fn1--->', name, age)
return 'ret1'
})
hook.tap('fn2', function (name, age) {
console.log('fn2--->', name, age)
return 'ret2'
})
hook.tap('fn3', function (name, age) {
console.log('fn3--->', name, age)
return 'ret3'
})
hook.call('jerry', 33)
// fn1---> jerry 33
// fn2---> ret1 33
// fn3---> ret2 33
- SyncLoopHook
執(zhí)行時,如果都沒有返回逞频,則執(zhí)行一遍后就退出
如果有一個返回了非undefined纯衍,則從頭重新開始執(zhí)行
當遇到返回了undefined則執(zhí)行完所有監(jiān)聽后退出
const { SyncLoopHook } = require('tapable')
let hook = new SyncLoopHook(['name', 'age'])
let count1 = 0
let count2 = 0
hook.tap('fn1', function (name, age) {
console.log('fn1--->', name, age)
if (++count1 === 1) {
count1 = 0
return undefined
}
return true
})
hook.tap('fn2', function (name, age) {
console.log('fn2--->', name, age)
if (++count2 === 2) {
count2 = 0
return undefined
}
return true
})
hook.tap('fn3', function (name, age) {
console.log('fn3--->', name, age)
})
hook.call('foo', 100)
// 不是undefined時候從上到下繼續(xù)依次執(zhí)行,直到返回undefined
// fn1---> foo 100
// fn2---> foo 100
// fn1---> foo 100
// fn2---> foo 100
// fn3---> foo 100
四苗胀、異步鉤子
對于異步鉤子的使用襟诸,在添加事件監(jiān)聽時會存在三種方式: tap、tapAsync基协、 tapPromise
- AsyncParalleHook 異步并行
const { AsyncParallelHook } = require('tapable')
let hook = new AsyncParallelHook(['name'])
// 對于異步鉤子的使用歌亲,在添加事件監(jiān)聽時會存在三種方式: tap tapAsync tapPromise
// hook.tap('fn1', function (name) {
// console.log('fn1--->', name)
// })
// hook.tap('fn2', function (name) {
// console.log('fn2--->', name)
// })
// hook.callAsync('zoe', function () {
// console.log('最后執(zhí)行了回調操作')
// })
/* console.time('time')
hook.tapAsync('fn1', function (name, callback) {
setTimeout(() => {
console.log('fn1--->', name)
callback()
}, 1000)
})
hook.tapAsync('fn2', function (name, callback) {
setTimeout(() => {
console.log('fn2--->', name)
callback()
}, 2000)
})
hook.callAsync('lg', function () {
console.log('最后一個回調執(zhí)行了')
console.timeEnd('time')
}) */
// 03 promise
console.time('time')
hook.tapPromise('fn1', function (name) {
return new Promise(function (resolve, reject) {
setTimeout(() => {
console.log('fn1--->', name)
resolve()
}, 1000)
})
})
hook.tapPromise('fn2', function (name) {
return new Promise(function (resolve, reject) {
setTimeout(() => {
console.log('fn2--->', name)
resolve()
}, 2000)
})
})
hook.promise('foo').then(() => {
console.log('end執(zhí)行了')
console.timeEnd('time')
})
// fn1---> foo
// fn2---> foo
// end執(zhí)行了
// time: 2.007s
- AsyncParallelBailHook 異步并行
const { AsyncParallelBailHook } = require('tapable')
let hook = new AsyncParallelBailHook(['name'])
console.time('time')
hook.tapAsync('fn1', function (name, callback) {
setTimeout(() => {
console.log('fn1--->', name)
callback()
}, 1000)
})
hook.tapAsync('fn2', function (name, callback) {
setTimeout(() => {
console.log('fn2--->', name)
callback('err')
}, 2000)
})
hook.tapAsync('fn3', function (name, callback) {
setTimeout(() => {
console.log('fn3--->', name)
callback()
}, 3000)
})
hook.callAsync('zce', function () {
console.log('最后的回調執(zhí)行了')
console.timeEnd('time')
})
// fn1---> zce
// fn2---> zce
// 最后的回調執(zhí)行了
// time: 2.004s
// fn3---> zce 執(zhí)行fn3是因為定時器到時間了,正常是不會執(zhí)行的
- AsyncSeriesHook 異步串行澜驮。 其他異步串行還有AsyncSeriesBailHook陷揪、AsyncSeriesLoopHook、AsyncSeriesWaterfallHook杂穷。
const { AsyncSeriesHook } = require('tapable')
let hook = new AsyncSeriesHook(['name'])
console.time('time')
hook.tapPromise('fn1', function (name) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('fn1--->', name)
resolve()
}, 1000)
})
})
hook.tapPromise('fn2', function (name) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('fn2--->', name)
resolve()
}, 2000)
})
})
hook.promise('foo').then(function () {
console.log('~~~~')
console.timeEnd('time')
})
// fn1---> foo
// fn2---> foo
// ~~~~
// time: 3.029s