webpack源碼分析2

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)

/**

  • 01 實例化 hook , 定義 _x = [f1, f2, ...] taps = [{}, {}]
  • 02 實例調用 tap taps = [{}, {}]
  • 03 調用 call 方法, HookCodeFactory setup create
  • 04 Hook SyncHook HookCodeFactory
    */
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
相關文章
相關標籤/搜索