1五、webpack與tapablewebpack
事件驅動型事件流工做機制web
負責建立bundles的compilationpromise
tapable自己就是一個獨立的庫併發
實例化hook註冊時間監聽app
經過hook出發事件監聽異步
執行懶編譯函數
hook本質就是tapable實例對象oop
hook執行機制分爲同步和異步ui
Hook執行特色:this
普通鉤子:監聽器之間互相不干擾
bailhook:熔斷鉤子,某個監聽返回undefined時後續不執行
waterfallhook:瀑布鉤子,上一個監聽返回值能夠傳給下一個
loophook:循環鉤子,當前監聽未返回false則一直執行
上述都有同步鉤子,例如SyncHook
AsyncSeriesHook
異步串行鉤子
異步並行鉤子
1六、同步鉤子的使用
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('zoe', 18)//經過call方法調用 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 })//返回undefined,fn3就不執行了 hook.tap('fn3', function (name, age) { console.log('fn3--->', name, age) }) hook.call('lg', 100) 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) { //這裏的name拿到了ret1 console.log('fn2--->', name, age) return 'ret2' }) hook.tap('fn3', function (name, age) { console.log('fn3--->', name, age) return 'ret3' }) hook.call('zce', 33) const { SyncLoopHook } = require('tapable') let hook = new SyncLoopHook(['name', 'age']) let count1 = 0 let count2 = 0 let count3 = 0 hook.tap('fn1', function (name, age) { console.log('fn1--->', name, age) if (++count1 === 1) { count1 = 0 return undefined//若是不返回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)
1七、異步鉤子
const { AsyncParallelHook } = require('tapable') //並行是指 等待全部併發的異步事件執行以後再執行最終的異步回調 let hook = new AsyncParallelHook(['name']) // 對於異步鉤子的使用,在添加事件監聽時會存在三種方式: 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('最後執行了回調操做') // }) /* 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('最後一個回調執行了') 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執行了') console.timeEnd('time') }) //最終輸出: 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’)//這邊就會提早結束hook.callAsync的執行 }, 2000) }) hook.tapAsync('fn3', function (name, callback) { setTimeout(() => { console.log('fn3--->', name) callback() }, 3000) }) hook.callAsync('zce', function () { console.log('最後的回調執行了') console.timeEnd('time') }) 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') })
1九、手寫異步鉤子
const SyncHook = require('./SyncHook.js')
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('zoe66', 18)
/**
let Hook = require('./Hook.js') class HookCodeFactory { args() { return this.options.args.join(',') // ["name", "age"]===> name, age } head() { return `var _x = this._x;` } content() { let code = `` for (var i = 0; i < this.options.taps.length; i++) { code += `var _fn${i} = _x[${i}];_fn${i}(${this.args()});` } return code } setup(instance, options) { // 先準備後續須要使用到的數據 this.options = options // 這裏的操做在源碼中是經過 init 方法實現,而咱們當前是直接掛在了 this 身上 instance._x = options.taps.map(o => o.fn) // this._x = [f1, f2, ....] } create() { // 核心就是建立一段可執行的代碼體而後返回 let fn // fn = new Function("name, age", "var _x = this._x, var _fn0 = _x[0]; _fn0(name, age);") // 在content中拼接出對應的代碼片斷 fn = new Function( this.args(), this.head() + this.content() ) return fn } } let factory = new HookCodeFactory() class SyncHook extends Hook { constructor(args) { super(args) } compile(options) { // {taps: [{}, {}], args: [name, age]} factory.setup(this, options) return factory.create(options) } } module.exports = SyncHook
./Hook.js class Hook { constructor(args = []) { this.args = args this.taps = [] // 未來用於存放組裝好的 {}對象信息 this._x = undefined // 未來在代碼工廠函數中會給 _x = [f1, f2, f3....] } tap(options, fn) { if (typeof options === 'string') { options = { name: options } } options = Object.assign({ fn }, options) // { fn:... name:fn1 } // 調用如下方法將組裝好的 options 添加至this.taps this._insert(options) } _insert(options) { this.taps[this.taps.length] = options } call(...args) { // 01 建立未來要具體執行的函數代碼結構 let callFn = this._createCall() // 02 調用上述的函數(args傳入進去) return callFn.apply(this, args) } _createCall() { return this.compile({ taps: this.taps, args: this.args }) } } module.exports = Hook
20、手寫同步鉤子
class Hook { constructor(args = []) { this.args = args this.taps = [] // 未來用於存放組裝好的 {} this._x = undefined // 未來在代碼工廠函數中會給 _x = [f1, f2, f3....] } tap(options, fn) { if (typeof options === 'string') { options = { name: options } } options = Object.assign({ fn }, options) // { fn:... name:fn1 } // 調用如下方法將組裝好的 options 添加至 [] this._insert(options) } tapAsync(options, fn) { if (typeof options === 'string') { options = { name: options } } options = Object.assign({ fn }, options) // { fn:... name:fn1 } // 調用如下方法將組裝好的 options 添加至 [] this._insert(options) } _insert(options) { this.taps[this.taps.length] = options } call(...args) { // 01 建立未來要具體執行的函數代碼結構 let callFn = this._createCall() // 02 調用上述的函數(args傳入進去) return callFn.apply(this, args) } callAsync(...args) { let callFn = this._createCall() return callFn.apply(this, args) } _createCall() { return this.compile({ taps: this.taps, args: this.args }) } } module.exports = Hook
let Hook = require('./Hook.js') class HookCodeFactory { args({ after, before } = {}) { let allArgs = this.options.args if (before) allArgs = [before].concat(allArgs) if (after) allArgs = allArgs.concat(after) return allArgs.join(',') // ["name", "age"]===> name, age } head() { return `"use strict";var _context;var _x = this._x;` } content() { let code = `var _counter = ${this.options.taps.length};var _done = (function () { _callback(); });` for (var i = 0; i < this.options.taps.length; i++) { code += `var _fn${i} = _x[${i}];_fn${i}(name, age, (function () { if (--_counter === 0) _done(); }));` } return code } setup(instance, options) { // 先準備後續須要使用到的數據 this.options = options // 這裏的操做在源碼中是經過 init 方法實現,而咱們當前是直接掛在了 this 身上 instance._x = options.taps.map(o => o.fn) // this._x = [f1, f2, ....] } create() { // 核心就是建立一段可執行的代碼體而後返回 let fn // fn = new Function("name, age", "var _x = this._x, var _fn0 = _x[0]; _fn0(name, age);") fn = new Function( this.args({ after: '_callback' }), this.head() + this.content() ) return fn } } let factory = new HookCodeFactory() class AsyncParallelHook extends Hook { constructor(args) { super(args) } compile(options) { // {taps: [{}, {}], args: [name, age]} factory.setup(this, options) return factory.create(options) } } module.exports = AsyncParallelHook