webpack tapable

tapable是什么

tapable是webpack的核心模块,是一个增强版的发布订阅库,是webpack plugin的基本实现方式。为使用者提供强大的hook机制。

tapable的主要api

const {
 SyncHook,
 SyncBailHook,
 SyncWaterfallHook,
 SyncLoopHook,
 AsyncParallelHook,
 AsyncParallelBailHook,
 AsyncSeriesHook,
 AsyncSeriesBailHook,
 AsyncSeriesWaterfallHook
 } = require("tapable");

tapable的分类

1.根据hook的类型可以分为同步Sync和异步Async,异步又可以分为并行和串行

  • Tapable
    • 同步Hook
      SyncHook
      SyncBailHook
      SyncWaterfallHook
      SyncLoopHook
    • 异步钩子
      • 异步并行
        AsyncParallelHook
        AsyncParallelBailHook
      • 异步串行
        AsyncSeriesHook
        AsyncSeriesBailHook
        AsyncSeriesWaterHook

2.hook的分类-按结果分

  • Tapable
    • Basic 按顺序执行每个事件函数,不关心函数的返回值
      SyncHook
      AsyncParallelHook
      AsyncSeriesHook
    • Bail 执行每个事件函数,遇到结果不为undefined则结束执行,直接走call的回调
      SyncBailHook
      AsyncParallelBailHook
      AsyncSeriesBailHook
    • Waterfall 将最近上一个函数的返回值作为下一个函数的参数,如果上一个函数没有返回值(返回undefined),那么就继续找上上个,如果找不到就用自己传入的参数
      SyncWaterfallHook
      AsyncSeriesWaterfallHook
    • Loop 不停的循环执行事件函数,直到所有函数结果 result === undefined,每次循环都是从头开始的SyncLoopHook

用法

注册事件回调

注册事件回调有三个方法: tap、tapAsync 和 tapPromise,其中 tapAsync 和 tapPromise 不能用于 Sync 开头的钩子类

触发事件

触发事件的三个方法是与注册事件回调的方法一一对应的,call 对应 tap、callAsync 对应 tapAsync 和 promise 对应 tapPromise。

SyncHook

//按顺序执行每个事件函数,不关心函数的返回值
const { SyncHook } = require('tapable');
const hook = new SyncHook();

hook.tap('first', () => {
  console.log('first');
});

hook.tap(
  'second', 
  () => {
    console.log('second');
  }
);

hook.call('call');

/**
 * output:
 * 
 * first
 * second
 */

AsyncParallelHook

const { AsyncParallelHook } = require('tapable');
const hook = new AsyncParallelHook(['name']);

hook.tapAsync('first', (name, callback) => {
  console.log('first', name, callback);
  // 必须执行否则不会执行后续的回调 继续调用second
  callback();
});
hook.tapAsync('second', (name, callback) => {
  console.log('second', name, callback);
  // 执行 callAsync 传入的回调
  callback(name);
});
// callAsync函数的params 对应tapAsync函数的第二个参数 回调函数的name
hook.callAsync('params', (error) => {
    console.log('callAsync', error);
});

/**
 * output:
 * 
 * second params [Function (anonymous)]
 * callAsync params
 * first params [Function (anonymous)]
 */

AsyncSeriesHook

const { AsyncSeriesHook } = require('tapable');
const hook = new AsyncSeriesHook(['name']);

hook.tapAsync('first', (name, callback) => {
  console.log('first', name, callback);
  // 必须执行否则不会执行后续的回调 继续调用second
  callback();
});
hook.tapAsync('second', (name, callback) => {
  console.log('second', name, callback);
  // 执行 callAsync 传入的回调
  callback(name);
});
// callAsync函数的params 对应tapAsync函数的第二个参数 回调函数的name
hook.callAsync('params', (error) => {
    console.log('callAsync', error);
});

/**
 * Console output:
 * 
 * first params [Function]
 * second params [Function]
 * callAsync undefined
 */
/**---------------------------------*/
  const { AsyncSeriesHook } = require('tapable');
  const queue = new AsyncSeriesHook(['name']);
  queue.tapPromise("1", function (name) {
    return new Promise(function (resolve) {
      setTimeout(function () {
        console.log(1, name);
        resolve();
      }, 1000);
    });
  });
  queue.tapPromise("2", function (name) {
    return new Promise(function (resolve, reject) {
      setTimeout(function () {
        console.log(2, name);
        reject('e');
      }, 2000);
    });
  });
  queue.tapPromise("3", function (name) {
    return new Promise(function (resolve) {
      setTimeout(function () {
        console.log(3, name);
        resolve();
      }, 3000);
    });
  });
  queue.promise("yh").then((data) => {
    console.log(data);
  }, (err) => {
    console.log(err);
  });
/**
 * Console output:
 * 
 * 1 yh
 * 2 yh
 * e
 */

SyncBailHook

只要回调函数中有一个返回不为undefined的就调过剩余的回到函数

const { SyncBailHook } = require('tapable')

const syncBailHook = new SyncBailHook(['name', 'age']);

syncBailHook.tap('1', (name, age) => {
  console.log(1, name, age)
})
syncBailHook.tap('2', (name, age) => {
  console.log(2, name, age)
  return '2'
})
syncBailHook.tap('3', (name, age) => {
  console.log(3, name, age)
})

syncBailHook.call('yh', 18)

/**
 * Console output:
 * 1 yh 18
 * 2 yh 18
 */

// 假如把2换为 下面的例子
syncBailHook.tap('2', (name, age) => {
  console.log(2, name, age)
  return null
})

/**
 * Console output:
 * 1 yh 18
 * 2 yh 18
 */

AsyncParallelBailHook

const { AsyncParallelBailHook } = require('tapable')

const queue = new AsyncParallelBailHook(['name']);

queue.tapAsync("1", function (name, callback) {
    console.log(1);
    callback();
  });
  queue.tapAsync("2", function (name, callback) {
    console.log(2);
    callback('s');
  });
  queue.tapAsync("3", function (name, callback) {
    console.log(3);
    callback();
  });
  queue.callAsync("yh", (err) => {
    console.log('call',err);
  });

/**
 * 1
 * 2
 * call s
 */
  

AsyncSeriesBailHook

queue.tapAsync("1", function (name, callback) {
  setTimeout(function () {
    console.log(1);
    callback("wrong");
  }, 1000);
});
queue.tapAsync("2", function (name, callback) {
  setTimeout(function () {
    console.log(2);
    callback();
  }, 2000);
});
queue.tapAsync("3", function (name, callback) {
  setTimeout(function () {
    console.log(3);
    callback();
  }, 3000);
});
queue.callAsync("yh", (err) => {
  console.log(err);
});
/**
 * 1
 * wrong
*/

SyncWaterfallHook

const { SyncWaterfallHook } = require('tapable')

const syncWaterfallHook = new SyncWaterfallHook(['name', 'age']);

syncWaterfallHook.tap('1', (name, age) => {
  console.log(1, name, age)
  return 'test1'
})
syncWaterfallHook.tap('2', (name, age) => {
  console.log(2, name, age)
  return 'test2'
})
syncWaterfallHook.tap('3', (name, age) => {
  console.log(3, name, age)
  return 'test3'
})
syncWaterfallHook.tap('4', (name, age) => {
  console.log(4, name, age)
})

syncWaterfallHook.call('yh', 18)

/**
 * output
 * 1 yh 18
 * 2 test1 18
 * 3 test2 18
 * 4 test3 18
*/

AsyncSeriesWaterfallHook

let { AsyncSeriesWaterfallHook } = require("tapable");
let queue = new AsyncSeriesWaterfallHook(["name", "age"]);

queue.tapAsync("1", function (name, age, callback) {
    setTimeout(function () {
      console.log(1, name, age);
      callback(null, 1);
    }, 1000);
  });
  queue.tapAsync("2", function (data, age, callback) {
    setTimeout(function () {
      console.log(2, data, age);
      callback('test2', 2);
    }, 2000);
  });
  queue.tapAsync("3", function (data, age, callback) {
    setTimeout(function () {
      console.log(3, data, age);
      callback(null, 3);
    }, 3000);
  });
  queue.callAsync("yh", 10, (err, data) => {
    console.log(err, data);
  });
  /**
   * 1 yh 10
   * 2 1 10
   * test2 undefined
  */

SyncLoopHook

let { SyncLoopHook } = require("tapable");
const syncLoopHook = new SyncLoopHook(['name', 'age']);

let counter1 = 0;
let counter2 = 0;


syncLoopHook.tap('1', (name, age) => {
  console.log(1, 'counter1', counter1, name, age);
  if (++counter1 < 5) {
    return 1;
  }
})
syncLoopHook.tap('2', (name, age) => {
  console.log(2, 'counter2', counter2, name, age);
})
syncLoopHook.call('yh', 18);
// output
// 1 counter1 0 yh 18
// 1 counter1 1 yh 18
// 1 counter1 2 yh 18
// 1 counter1 3 yh 18
// 1 counter1 4 yh 18
// 2 counter2 0 yh 18

// 源码拼接出来的可执行代码
// do {
//  _loop = false;
//  var _fn0 = _x[0];
//  var _result0 = _fn0(name, age);
//  if (_result0 !== undefined) {
//      _loop = true;
//  } else {
//      var _fn1 = _x[1];
//      var _result1 = _fn1(name, age);
//      if (_result1 !== undefined) {
//          _loop = true;
//      } else {
//          if (!_loop) {}
//      }
//  }
// } while (_loop);

参考

https://blog.csdn.net/qq_17175013/article/details/119547711

https://zhuanlan.zhihu.com/p/100974318

https://www.infoq.cn/article/lcpop63kdeosott5fvjx

你可能感兴趣的:(webpack tapable)