介绍

tapable 是一个类似于nodejs 的EventEmitter 的库, 主要是控制钩子函数的发布与订阅,控制着webpack的插件系.webpack的本质就是一系列的插件运行.

  1. const {
  2. SyncHook,
  3. SyncBailHook,
  4. SyncWaterfallHook,
  5. SyncLoopHook,
  6. AsyncParallelHook,
  7. AsyncParallelBailHook,
  8. AsyncSeriesHook,
  9. AsyncSeriesBailHook,
  10. AsyncSeriesWaterfallHook
  11. } = require("tapable");
  • SyncHook所有tap注册的函数都会执行
  • SyncBailHook某一个tap注册的函数有返回值,则后面注册的都不会执行
  • SyncWaterfallHook瀑布的意思就是上一个监听函数的结果是下一个人的输入
  • SyncLoopHook 所有tap注册的函数都会执行,只是某个监听事件 如果返回了值 这个方法会再次执行,只有返回undefined这个方法才会停止执行
  • AsyncParallelHook
  • AsyncParallelBailHook
  • AsyncSeriesHook
  • AsyncSeriesBailHook
  • AsyncSeriesWaterfallHook

1 SyncHook

所有tap注册的函数都会执行

1.1 使用

  1. // 同步的方法
  2. let {SyncHook} = require('tapable');
  3. // 核心就是发布订阅
  4. class Lesson {
  5. constructor(){
  6. this.hooks = {
  7. arch: new SyncHook(['name','age']) // 限制绑定函数的参数
  8. }
  9. }
  10. tap(){ // 希望调用这个方法来在钩子上注册事件
  11. this.hooks.arch.tap('node', function (name) { // 第一个参数是注释作用 没有实际意义
  12. console.log('node', name);
  13. });
  14. this.hooks.arch.tap('react', function (name) {
  15. console.log('react', name);
  16. });
  17. }
  18. start(){
  19. this.hooks.arch.call('jw');
  20. }
  21. }
  22. let l = new Lesson();
  23. l.tap();
  24. l.start();
  25. /*
  26. node jw
  27. react jw
  28. */

1.2 手写

  1. class SyncHook{ // 同步的钩子
  2. constructor(args) { // args 起一个限制作用
  3. this.tasks = [];
  4. }
  5. tap(name,task){
  6. this.tasks.push(task);
  7. }
  8. call(...args){
  9. this.tasks.forEach(task=>task(...args));
  10. }
  11. }
  12. let hook = new SyncHook(['name']);
  13. hook.tap('node',function (name) {
  14. console.log('node',name);
  15. }); // tap用来注册事件的 events.on
  16. hook.tap('react', function (name) {
  17. console.log('react', name);
  18. });
  19. hook.call('jw');

2 SyncBailHook

某一个tap注册的函数有返回值,则后面注册的都不会执行

2.1 使用

  1. // 同步的方法
  2. let {SyncBailHook} = require('tapable');
  3. // Bail保险 熔断型的
  4. // 核心就是发布订阅
  5. class Lesson {
  6. constructor(){
  7. this.hooks = {
  8. arch: new SyncBailHook(['name','age']) // 限制绑定函数的参数
  9. }
  10. }
  11. tap(){ // 希望调用这个方法来在钩子上注册事件
  12. this.hooks.arch.tap('node', function (name) { // 第一个参数是注释作用 没有实际意义
  13. console.log('node', name);
  14. return false; // 如果当前函数有返回值则不会继续执行
  15. });
  16. this.hooks.arch.tap('react', function (name) {
  17. console.log('react', name);
  18. });
  19. }
  20. start(){
  21. this.hooks.arch.call('jw');
  22. }
  23. }
  24. let l = new Lesson();
  25. l.tap();
  26. l.start();
  27. // node jw

2.2 手写

  1. class SyncBailHook{ // 同步的钩子
  2. constructor(args) { // args 起一个限制作用
  3. this.tasks = [];
  4. }
  5. tap(name,task){
  6. this.tasks.push(task);
  7. }
  8. call(...args){
  9. let index = 0; // 取任务队列中的第一个
  10. let ret;
  11. do{ // 至少做一次
  12. ret = this.tasks[index++](...args);
  13. }while(ret === undefined && index < this.tasks.length)
  14. }
  15. }
  16. let hook = new SyncBailHook(['name']);
  17. hook.tap('node',function (name) {
  18. console.log('node',name);
  19. // return '停一停有事'
  20. }); // tap用来注册事件的 events.on
  21. hook.tap('react', function (name) {
  22. console.log('react', name);
  23. });
  24. hook.call('jw');

3 SyncWaterfallHook

瀑布的意思就是上一个监听函数的结果是下一个人的输入

3.1 使用

  1. // 同步的方法
  2. let {SyncWaterfallHook} = require('tapable');
  3. // 瀑布的意思就是上一个监听函数的结果是下一个人的输入
  4. class Lesson {
  5. constructor(){
  6. this.hooks = {
  7. arch: new SyncWaterfallHook(['name','age'])
  8. }
  9. }
  10. tap(){ // 希望调用这个方法来在钩子上注册事件
  11. this.hooks.arch.tap('node', function (name) {
  12. console.log('node', name);
  13. return 'node学的不错'
  14. });
  15. this.hooks.arch.tap('react', function (data) {
  16. console.log('react', data);
  17. return 'react 更好'
  18. });
  19. this.hooks.arch.tap('webpack', function (data) {
  20. console.log('webpack', data);
  21. });
  22. }
  23. start(){
  24. this.hooks.arch.call('jw');
  25. }
  26. }
  27. let l = new Lesson();
  28. l.tap();
  29. l.start();
  30. /*
  31. node jw
  32. react node学的不错
  33. webpack react 更好
  34. */

3.2 手写

class SyncWaterfallHook{ 
  constructor(args) { 
    this.tasks = [];
  }
  tap(name,task){
    this.tasks.push(task);
  }
  call(...args){
    let [first,...others] = this.tasks;
    // first就是第一个任务
    // reduce
    others.reduce((prev,next)=>{
      return next(prev);
    }, first(...args))
  }
}
let hook = new SyncWaterfallHook(['name']);
hook.tap('node',function (name) {
  console.log('node',name);
  return 'node还不错'
}); 
hook.tap('react', function (data) {
  console.log('react', data);
  return 'react ok'
}); 
hook.tap('webpack', function (data) {
  console.log('webpack', data);
}); 
hook.call('jw');

4 SyncLoopHook

所有tap注册的函数都会执行,只是某个监听事件 如果返回了值 这个方法会再次执行,只有返回undefined这个方法才会停止执行

4.1 使用

let {SyncLoopHook} = require('tapable');
// 某个监听事件 如果返回了值 这个方法会再次执行,只有返回undefined这个方法才会停止执行
class Lesson {
  constructor(){
    this.index = 0;
    this.hooks = {
      arch: new SyncLoopHook(['name','age']) 
    }
  }
  tap(){ 
    this.hooks.arch.tap('node',  (name) => { 
      console.log('node', name);
      return ++this.index == 3?undefined:'再来一次'
    });
    this.hooks.arch.tap('react',  (data) =>{
      console.log('react', data);
    });
    this.hooks.arch.tap('webpack', function (data) {
      console.log('webpack', data);
    });
  }
  start(){
    this.hooks.arch.call('jw');
  }
}
let l = new Lesson();
l.tap();
l.start();
/*
node jw
node jw
node jw
react jw
webpack jw

*/

4.2 手写

class SyncLoopHook{ 
  constructor(args) { 
    this.tasks = [];
  }
  tap(name,task){
    this.tasks.push(task);
  }
  call(...args){
    this.tasks.forEach(task => {
      let ret;
      do{
        ret = task(...args);
      } while (ret !== undefined)
    });
  }
}
let total = 0;
let hook = new SyncLoopHook(['name']);
hook.tap('node',function (name) {
  console.log('node',name);
  return ++total == 3?undefined :'1'
}); 
hook.tap('react', function (name) {
  console.log('react', name);

}); 
hook.call('jw');

5 AsyncParallelHook

使用

有点像promise.all那种感觉

let { AsyncParallelHook } = require('tapable');
class Lesson {
  constructor() {
    this.hooks = {
      arch: new AsyncParallelHook(['name'])
    }
  }
  tap() {
    this.hooks.arch.tapAsync('react', (name, cb) => {
      setTimeout(() => {
        console.log('react', name);
        cb(); 
      }, 2000);
    });
    this.hooks.arch.tapAsync('react', (name, cb) => {
      setTimeout(() => {
        console.log('react', name);
        cb();
      }, 2000);
    });
  }
  start() {
    this.hooks.arch.callAsync('jw', function () {
      console.log('end');
    });
  }
}
let l = new Lesson();
l.tap();
l.start();
/*
node jw
react jw
end

*/

某一个tap有返回值时,则结束

let { AsyncParallelHook } = require('tapable');
class Lesson {
  constructor() {
    this.hooks = {
      arch: new AsyncParallelHook(['name'])
    }
  }
  tap() {
    this.hooks.arch.tapAsync('react', (name, cb) => {
      setTimeout(() => {
        console.log('react', name);
        cb('出错了'); // BailHook
      }, 2000);
    });
    this.hooks.arch.tapAsync('node', (name, cb) => {
      setTimeout(() => {
        console.log('node', name);
        cb();
      }, 1000);
    });
    this.hooks.arch.tapAsync('vue', (name, cb) => {
      setTimeout(() => {
        console.log('vue', name);
        cb();
      }, 3000);
    });
  }
  start() {
    this.hooks.arch.callAsync('jw', function () {
      console.log('end');
    });
  }
}
let l = new Lesson();
l.tap();
l.start();
/*

node jw
react jw
end
vue jw

*/

用promise

let { AsyncParallelBailHook } = require('tapable'); // 实现AsyncParallelBailHook
class Lesson {
  constructor() {
    this.hooks = {
      arch: new AsyncParallelBailHook(['name'])
    }
  }
  tap() {
    this.hooks.arch.tapPromise('react', (name) => {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log('react', name);
          resolve();
        }, 2000);
      })
    });
    this.hooks.arch.tapPromise('node', (name) => {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log('node', name);
          resolve();
        }, 1000);
      })
    });
  }
  start() {
    this.hooks.arch.promise('jw').then(function () {
      console.log('end');
    });
  }
}
let l = new Lesson();
l.tap();
l.start();
/*
* node jw
* react jw
  end
* */

promise有返回值

let { AsyncParallelBailHook } = require('tapable'); // 实现AsyncParallelBailHook
class Lesson {
  constructor() {
    this.hooks = {
      arch: new AsyncParallelBailHook(['name'])
    }
  }
  tap() {
    this.hooks.arch.tapPromise('react', (name) => {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log('react', name);
          resolve('出错了');
        }, 2000);
      })
    });
    this.hooks.arch.tapPromise('node', (name) => {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log('node', name);
          resolve();
        }, 1000);
      })
    });

    this.hooks.arch.tapPromise('vue', (name) => {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log('vue', name);
          resolve();
        }, 3000);
      })
    });
  }
  start() {
    this.hooks.arch.promise('jw').then(function () {
      console.log('end');
    });
  }
}
let l = new Lesson();
l.tap();
l.start();
/*
  node jw
  react jw
  end
  vue jw
*/

6 AsyncSeriesHook

串行执行,写在前面的先执行

使用

let { AsyncSeriesHook } = require('tapable');
// AsyncSeriesHook 串行执行 tapAsync + callAsync
//                         tapPromise + promise
class Lesson {
  constructor() {
    this.hooks = {
      arch: new AsyncSeriesHook(['name'])
    }
  }
  tap() {
    this.hooks.arch.tapPromise('react', (name) => {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log('react', name);
          resolve();
        }, 2000);
      })
    });
    this.hooks.arch.tapPromise('node', (name) => {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log('node', name);
          resolve();
        }, 1000);
      })
    });
  }
  start() {
    this.hooks.arch.promise('jw').then(function () {
      console.log('end');
    });
  }
}
let l = new Lesson();
l.tap();
l.start();
/*
react jw
node jw
end
*/

手写

class AsyncSeriesHook {
  constructor() {
    this.tasks = [];
  }
  tapPromise(name, task) {
    this.tasks.push(task);
  }
  promise(...args) { 
    let [first,...others] = this.tasks;
    return others.reduce((p,n)=>{
      return p.then(()=>n()); // 把所有的promise串连起来
    }, first(...args));
  }
}
let hook = new AsyncSeriesHook(['name']);
hook.tapPromise('node', function (name) {
  return new Promise((resolve,reject)=>{
    setTimeout(() => {
      console.log('node');
      resolve();
    }, 1000);
  })
});
hook.tapPromise('react', function (name) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('react');
      resolve();
    }, 1000);
  })
});
hook.promise('jw').then(function () {
  console.log('all')
});

7 AsyncSeriesBailHook

7.1 使用

let {AsyncSeriesWaterfallHook} = require('tapable');
// AsyncSeriesBailHook
// tapable (compose + promise + asyncCallback)
// webpack
let h = new AsyncSeriesWaterfallHook(['name']);
// tapAsync tapPromise
h.tapAsync('node',function (name,cb) {
  setTimeout(() => {
    console.log('node');
    cb(null,'我饿了');
  }, 1000);
});
h.tapAsync('react', function (data, cb) {
  setTimeout(() => {
    console.log('react', data);
    cb();
  },2000);
});

h.callAsync('jw',function () {
  console.log('end');
});

7.2 手写

// let {AsyncSerieslHook} = require('tapable');
class AsyncSerieslHook{
  constructor(){
    this.tasks = [];
  }
  tapAsync(name,task){
    this.tasks.push(task);
  }
  callAsync(...args){ // express 中的中间件的原理
    let finalCallback = args.pop();
    let index = 0;
    let next = ()=>{ // compose
      if (index === this.tasks.length) return finalCallback();
      let task = this.tasks[index++];
      task(...args,next);
    }
    next();
  }
}
let hook = new AsyncSerieslHook(['name']);
hook.tapAsync('node',function (name,cb) {
  setTimeout(() => {
    console.log('node');
    cb('错误');
  }, 1000);
});
hook.tapAsync('react', function (name,cb) {
  setTimeout(() => {
    console.log('react');
    cb();
  }, 1000);
});

hook.callAsync('jw',function () {
  console.log('all')
});