关于javascript:webpack源码分析2

6次阅读

共计 7459 个字符,预计需要花费 19 分钟才能阅读完成。

15、webpack 与 tapable

事件驱动型事件流工作机制

负责创立 bundles 的 compilation

tapable 自身就是一个独立的库

实例化 hook 注册工夫监听

通过 hook 登程事件监听

执行懒编译

hook 实质就是 tapable 实例对象

hook 执行机制分为同步和异步

Hook 执行特点:

一般钩子:监听器之间相互不烦扰

bailhook:熔断钩子,某个监听返回 undefined 时后续不执行

waterfallhook:瀑布钩子,上一个监听返回值能够传给下一个

loophook:循环钩子,以后监听未返回 false 则始终执行

上述都有同步钩子,例如 SyncHook

AsyncSeriesHook

异步串行钩子

异步并行钩子

16、同步钩子的应用


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)

17、异步钩子


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')
})

19、手写异步钩子

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
正文完
 0