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拿到了ret1console.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 = 0let count2 = 0let count3 = 0hook.tap('fn1', function (name, age) {console.log('fn1--->', name, age)if (++count1 === 1) {count1 = 0return 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 promiseconsole.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.jsclass 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