tapable详解

tapable详解

tapable是webpack内部使用的一个流程管理工具,主要用来串联插件,完善事件流执行。

1.安装tapable

yarn add tapable

2. 常用hooks

import {
    SyncHook,
    SyncBailHook,
    SyncWaterfallHook,
    SyncLoopHook,
    AsyncParallelHook,
    AsyncParallelBailHook,
    AsyncSeriesHook,
    AsyncSeriesBailHook,
    AsyncSeriesWaterfallHook
 } form 'tapable';

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gBgy8rW0-1611562755204)(D:\download\tapable.png)]

tapable通过tap注册一个事件,通过call执行该钩子注册的所有事件。

tapable的每个hooks都tap一个或多个事件。

tapAsync/callAsynctapPromise/Promise用于注册同步执行的异步事件,callAsync用在并行执行的异步钩子完成后再执行该函数。

用法示例:

new SyncHook([arg1,arg2,...])

hooks接收一个数组参数,参数为执行回调事件所需的参数名。

  • call:(...args) => void当你的钩子触发之前,(就是call()之前),就会触发这个函数,你可以访问钩子的参数.多个钩子执行一次

  • tap: (tap: Tap) => void 每个钩子执行之前(多个钩子执行多个),就会触发这个函数

  • loop:(...args) => void 这个会为你的每一个循环钩子(LoopHook, 就是类型到Loop的)触发,具体什么时候没说

  • register:(tap: Tap) => Tap | undefined 每添加一个Tap都会触发 你interceptor上的register,你下一个拦截器的register 函数得到的参数 取决于你上一个register返回的值,所以你最好返回一个 tap 钩子.

2.1 SyncHook

同步串行,在触发事件之后,会依次执行注册的所有事件处理函数。其原理是将监听(订阅)的函数存放到一个数组中, 发布时遍历数组中的监听函数并且将发布时的 arguments传递给监听函数。

不关心返回值,从上到下依次执行注册事件。

  const hook = new SyncHook(['name', 'sex'])
  /*
  tap(options,function):
  options是事件描述,可以为一个字符串,也可以为一个对象,为对象时必须包含name属性,描述该插件名称。
  function:回调函数
  */
  // 打印我的名字
  hook.tap('printName', (name) => {
    console.log('my name is ' + name);
  })
  hook.tap('printSex', (name, sex) => {
    console.log('I’m a ' + sex);
  })
  // call(arg1,arg2,...)
  hook.call('张三', 'man');

执行结果:

my name is 张三

I’m a man

Basic Hook:依次执行注册事件,无法中断。

2.2 SyncBailHook

同步串行,当注册事件无返回值,或者返回undefined时继续执行之后的注册事件,否则中断执行。

  const hook = new SyncBailHook(['name', 'sex', 'age'])
  /*
  tap(options,function):
  options是事件描述,可以为一个字符串,也可以为一个对象,为对象时必须包含name属性,描述该插件名称。
  function:回调函数
  */
  // 打印我的名字
  hook.tap('printName', (name) => {
    const nameStr = 'my name is ' + name;
    console.log(nameStr);
    return;
  })
  hook.tap('printSex', (name, sex) => {
    const sexStr = 'I’m a ' + sex;
    console.log(sexStr);
    return sexStr;
  })
  hook.tap('printAge', (name, sex, age) => {
    const ageStr = 'I’m ' + age + ' years old';
    console.log(ageStr);
  })
  // call(arg1,arg2,...)
  hook.call('张三', 'man', 29);

执行结果:

my name is 张三

I’m a man

Bail Hook:运行提前退出执行函数,当存在返回值不为undefined时,则不再进行之后的事件执行。

2.3 SyncWaterfallHook

同步瀑布流,依次执行注册的所有事件,并将上一个事件的执行结果作为下一个事件的入参。

  const hook = new SyncWaterfallHook(['name', 'sex', 'age'])
  /*
  tap(options,function):
  options是事件描述,可以为一个字符串,也可以为一个对象,为对象时必须包含name属性,描述该插件名称。
  function:回调函数
  */
  // 打印我的名字
  hook.tap('printName', (name, sex, age) => {
    const nameStr = 'my name is ' + name;
    console.log(nameStr);
    return { sex, age };
  })
  hook.tap('printSex', (data) => {
    const sexStr = 'I’m a ' + data.sex;
    console.log(sexStr);
    return data.age;
  })
  hook.tap('printAge', (age) => {
    const ageStr = 'I’m ' + age + ' years old';
    console.log(ageStr);
  })
  // call(arg1,arg2,...)
  hook.call('张三', 'man', 29);

执行结果:

my name is 张三

I’m a man

I’m 29 years old

Waterfall Hook:接收上一个注册事件的返回值作为下一个注册事件的参数。


2.4 SyncLoopHook

同步循环执行,当注册事件返回值非undefined时,循环执行该注册事件,直至返回undefined时,才继续执行下一个事件。

  const hook = new SyncLoopHook(['name', 'sex', 'age'])
  /*
  tap(options,function):
  options是事件描述,可以为一个字符串,也可以为一个对象,为对象时必须包含name属性,描述该插件名称。
  function:回调函数
  */
  // 打印我的名字
  let num = 0;
  hook.tap('printName', (name) => {
    const nameStr = 'my name is ' + name;
    console.log(nameStr);
    num++;
    return num === 3 ? undefined : false;
  })
  hook.tap('printSex', (name, sex) => {
    const sexStr = 'I’m a ' + sex;
    console.log(sexStr);
    return;
  })
  hook.tap('printAge', (name, sex, age) => {
    const ageStr = 'I’m ' + age + ' years old';
    console.log(ageStr);
  })
  // call(arg1,arg2,...)
  hook.call('张三', 'man', 29);

执行结果:

my name is 张三

my name is 张三

my name is 张三

I’m a man

I’m 29 years old

Loop Hook:循环执行注册事件,直至返回undefined才进入下一个注册事件。

2.5 AsyncParallelHook

异步并行执行,当所有注册事件都执行完成后,才执行callAsync或者promise

const hook = new AsyncParallelHook(['name', 'sex', 'age'])
  /*
  tap(options,function):
  options是事件描述,可以为一个字符串,也可以为一个对象,为对象时必须包含name属性,描述该插件名称。
  function:回调函数
  */
  // 打印我的名字
  let num = 0;
  console.time('time')
  hook.tapAsync('printName', (name, sex, age, next) => {
    setTimeout(() => {
      const nameStr = 'my name is ' + name;
      console.log(nameStr);
      next();
    }, 1000)
  })
  hook.tapAsync('printSex', (name, sex, age, next) => {
    setTimeout(() => {
      const sexStr = 'I’m a ' + sex;
      console.log(sexStr);
      next();
    }, 2000)
  })
  hook.tapAsync('printAge', (name, sex, age, next) => {
    setTimeout(() => {
      const ageStr = 'I’m ' + age + ' years old';
      console.log(ageStr);
      next();
    }, 3000)
  })
  // call(arg1,arg2,...)
  hook.callAsync('张三', 'man', 29,()=>{
    console.log('执行完成!');
    console.timeEnd('time');
  });

执行结果:

my name is 张三
I’m a man
I’m 29 years old
执行完成!
time: 3002.875732421875 ms

callAsync:最后一个参数接收一个回调函数,当所有的注册事件都执行完成后,才会执行该回调。

tapAsync:除了hook声明的参数,它还接收一个next()回调,当存在next时,当所有注册事件执行完成后则执行next回调。

Parallel Hook:异步并行执行,所有的异步事件都执行完成后,才执行callAsync/promise定义的回调。

2.6 AsyncSeriesHook

异步串行执行,与AsyncParallelHook类似,所有注册事件执行完成后才会执行callAsync/promise定义的回调。

  const hook = new AsyncSeriesHook(['name', 'sex', 'age'])
  /*
  tap(options,function):
  options是事件描述,可以为一个字符串,也可以为一个对象,为对象时必须包含name属性,描述该插件名称。
  function:回调函数
  */
  // 打印我的名字
  let num = 0;
  console.time('time')
  hook.tapPromise('printName', (name, sex, age) => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        const nameStr = 'my name is ' + name;
        console.log(nameStr);
        resolve('printName');
      }, 1000)
    })
  })
  hook.tapPromise('printSex', (name, sex, age) => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        const sexStr = 'I’m a ' + sex;
        console.log(sexStr);
        resolve('printSex')
      }, 2000)
    })
  })
  hook.tapPromise('printAge', (name, sex, age) => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        const ageStr = 'I’m ' + age + ' years old';
        console.log(ageStr);
        resolve('printAge')
      }, 3000)
    })
  })
  // call(arg1,arg2,...)
  hook.promise('张三', 'man', 29).then(res => {
    console.log(res);
    console.timeEnd('time');
  });

执行结果:

my name is 张三

I’m a man

I’m 29 years old

undefined

time: 6005.301025390625 ms

2.7 其他

  • AsyncParallelBailHook:异步并行且运行中断。
  • AsyncSeriesBailHook :异步串行且运行中断。
  • AsyncSeriesWaterfallHook:异步串行瀑布流,上一个事件的结果作为下一个事件的参数。

3.拦截器(interception)

所有的钩子函数都有额外的拦截器。

  • call:执行call/callAsync/promise时触发。
  • register:定义tap/tapAsync/tapPromise时触发。
  • tap:执行tap/tapAsync/tapPromise定义的内容时触发。
  • loop:执行SyncLoopHook注册的事件时触发。
  • error: 执行报错时触发。
  • result: 存在返回结果时触发,主要针对Waterfall Hook/Bail Hook,在call之前,tap之后触发。
  • done: 执行call/callAsync/promise完成后触发。
 const hook = new AsyncSeriesHook(['name', 'sex', 'age'])

  hook.intercept({
    call: (name, sex, age) => {
      console.log('执行拦截器');
    },
    register: (name) => {
      console.log('定义钩子函数时的拦截器');
    },
    tap: () => {
      console.log('执行钩子函数时的拦截器');
    },
    loop:()=>{
      console.log('循环事件函数执行时的拦截器');
    }
  })
  // 打印我的名字
  let num = 0;
  console.time('time')
  hook.tapPromise('printName', (name, sex, age) => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        const nameStr = 'my name is ' + name;
        console.log(nameStr);
        resolve('printName');
      }, 1000)
    })
  })
  hook.tapPromise('printSex', (name, sex, age) => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        const sexStr = 'I’m a ' + sex;
        console.log(sexStr);
        resolve('printSex')
      }, 2000)
    })
  })
  hook.tapPromise('printAge', (name, sex, age) => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        const ageStr = 'I’m ' + age + ' years old';
        console.log(ageStr);
        resolve('printAge')
      }, 3000)
    })
  })
  // call(arg1,arg2,...)
  hook.promise('张三', 'man', 29).then(res => {
    console.log(res);
    console.timeEnd('time');
  });

执行结果:

定义钩子函数时的拦截器
定义钩子函数时的拦截器
定义钩子函数时的拦截器
执行拦截器
执行钩子函数时的拦截器
my name is 张三
执行钩子函数时的拦截器
I’m a man
执行钩子函数时的拦截器
I’m 29 years old
undefined
time: 6005.77099609375 ms

4.HookMap

一个 HookMap是一个Hooks映射的帮助类。实际就是一个hook的key-value数组。


  const hookMap = new HookMap(key => new AsyncSeriesWaterfallHook(['name', 'sex', 'age']));
  hookMap.tapPromise('myPlugin', 'printName', (name, sex, age) => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        const nameStr = 'my name is ' + name;
        console.log(nameStr);
        resolve('printName');
      }, 1000)
    })
  });
  const hook = hookMap.get('myPlugin');
  // call(arg1,arg2,...)
  hook.promise('张三', 'man', 29).then(res => {
    console.log(res);
  });

5.MultiHook

把其他的Hook 重定向(转化)成为一个 MultiHook

import { MultiHook } from "tapable";

const hookA = new SyncHook(['name']);
const hookB = new SyncBailHook(['age']);
const allHooks = new MultiHook([hookA, hookB]);

.log(nameStr);
resolve(‘printName’);
}, 1000)
})
});
const hook = hookMap.get(‘myPlugin’);
// call(arg1,arg2,…)
hook.promise(‘张三’, ‘man’, 29).then(res => {
console.log(res);
});




## 5.MultiHook

把其他的Hook 重定向(转化)成为一个 MultiHook

import { MultiHook } from “tapable”;

const hookA = new SyncHook([‘name’]);
const hookB = new SyncBailHook([‘age’]);
const allHooks = new MultiHook([hookA, hookB]);


你可能感兴趣的:(webpack,tapable,webpack,插件制作)