webpack从入门到进阶(三)—— webpack核心库tapable使用

·  阅读 2032
webpack从入门到进阶(三)—— webpack核心库tapable使用

前言

tapable是个哈玩意呢?

简单来说,tapabale就是一个类似于EventEmitter的事件驱动,它包含事件监听器和事件触发器。只不过,tapable比EventEmitter更强大,它包含更多的事件类型和事件方法。

那凭啥说tapable是webpack的核心库呢?

webpack进行打包的时候,涉及到很多的 loader 和 plugin,那么webpack和它们之间是如何进行通信的呢?webpack的生命周期钩子(嘿嘿,其实就是webpack打包过程中的各个节点)又是怎么串联起来的呢?其实这一切都是tapable的功劳,这也是为什么说tapable是webpack的核心库。

tapable的使用

SyncHook的使用

  1. 实例化SyncHook

实例化时接收两个参数,分别是形参的key和hook的名称;事件的执行顺序是同步的。

new SyncHook(['name', 'remark'], 'coffee');
复制代码

注意,注意,注意,重要的事情说三遍:如果实例化时形参使用了两个参数,那么事件执行的时候接受的时候只有两个参数,即使触发事件的时候传进去的是三个参数。

  1. 添加事件监听器

tap事件有两个参数,一个是回调事件的名称或者回调事件的配置项,另一个是回调事件。回调事件接收的参数取决于实例化时所传入的形参。

this.hook.coffeeHook.tap('grind', (name) => {
  console.log(`grind ${name}`);
});
复制代码

另外,可以通过 before 和 stage 来调整回调事件的执行顺序。

  • before 的权重高于 stage
  • stage 使用负值表示执行顺序提前,使用正值表示执行顺序向后
  • stage 的绝对值越大,偏移的位置越大
  1. 触发事件监听器

触发事件监听器时,接收的实参与实例化时注册的形参相对应

this.hook.coffeeHook.call(name, remark);
复制代码
  1. 其它同步方法

SyncBailHook:只要当前执行的事件返回值不是undefined,那么后续的事件都不会执行

SyncWaterfallHook:如果当前执行的事件返回的值不为undefined,那么下一个事件的第一个参数替换为该返回值

SyncLoopHook:当前执行的事件回调的返回值不是 undefined 时,会重新从第一个注册的事件回调处执行,直到当前执行的事件回调没有返回值

AsyncParallelHook的使用

  1. 实例化AsyncParallelHook

实例化时接收两个参数,分别是形参的key和hook的名称;事件的执行顺序是并行的。

new AsyncParallelHook(['name', 'remark'], 'coffee');
复制代码
  1. 添加事件监听器

AsyncParallelHook支持通过 tap、tapAsync 和 tapPromise 三个方法注册监听事件;tap方法与SyncHook的相同,tapAsync我们先留个悬念,放到下面讲,我们这里只介绍一下tapPromise。

tapPromise事件有两个参数,一个是回调事件的名称或者回调事件的配置项,另一个是回调事件。回调事件接收的参数取决于实例化时所传入的形参。

this.hook.coffeeHook.tapPromise({
  name: 'brew',
}, (name, remark) => {
  console.log(`brew ${name}, ${remark}`);

  return Promise.reject('brew fail');
});
复制代码

tapPromise回调事件必须返回Promise,否则会报错Error: Tap function (tapPromise) did not return promise (returned undefined)

  1. 触发回调事件

触发事件监听器时,接收的实参与实例化时注册的形参相对应;

  • 通过callAsync触发
this.hook.coffeeHook.callAsync(name, remark);
复制代码
  • 通过promise触发
this.hook.coffeeHook
  .promise(name, remark)
  .then((data) => {
    console.log('data::::::::', data);
  })
  .catch(err => {
    console.log('err::::::::', err);
  });
复制代码
  1. 验证toPromise是并行的

在前一个回调事件中添加一个定时器,如果是顺序执行,那么前一个resolve之前,后一个回调事件不会执行的;反之,如果后一个回调事件先执行了,那说明toPromise就是并行的。

通过下述代码,得出结论:toPromise是并行的

this.hook.coffeeHook.tapPromise({
  name: 'boil',
}, (...agus) => {
  console.log('boil water');
  console.log('boil agus:::::::', agus);

  return new Promise((resolve) => {
    // 如果是同步执行的
    setTimeout(() => {
      console.log('boil success');
      resolve('success');
    }, 2000);
  });
});

this.hook.coffeeHook.tapPromise({
  name: 'brew',
}, (name, remark) => {
  console.log(`brew ${name}, ${remark}`);

  return Promise.resolve('brew success');
});

// boil water
// boil agus::::::: [ 'Blue Mountain Coffee', 'use suga' ]
// brew Blue Mountain Coffee, use suga
// garland
// garland agus::::::: [ 'Blue Mountain Coffee', 'use suga' ]
// boil success
复制代码
  1. 其它同步方法

AsyncParallelBailHook:只要当前执行的事件返回值不是undefined,那么后续的事件都不会执行

AsyncSeriesHook的使用

  1. 实例化AsyncSeriesHook

实例化时接收两个参数,分别是形参的key和hook的名称;事件的执行顺序是顺序的。

new AsyncParallelHook(['name', 'remark'], 'coffee');
复制代码
  1. 添加事件监听器

AsyncParallelHook支持通过 tap、tapAsync 和 tapPromise 三个方法注册监听事件;tap方法与tapPromise方法已经介绍过了,这里我们再介绍一下tapAsync。

tapAsync事件有两个参数,一个是回调事件的名称或者回调事件的配置项,另一个是回调事件。回调事件接收的参数取决于实例化时所传入的形参。

 this.hook.coffeeHook.tapAsync({
  name: 'brew',
}, async (name, callback) => {
  console.log(`brew ${name}`);

  await new Promise((resolve, reject) => {
    setTimeout(() => {
      if (Math.random() > 0.5) {
        resolve('brew success');
      } else {
        reject('brew fail');
      }
    }, 2000);
  })
    .then(result => {
      console.log('brew result::::::', result);
      callback(undefined, result);
    })
    .catch(err => {
      callback(err);
    });
});
复制代码
  • 回调事件接收一个callback
  • 如果不执行callback,那么后续的回调事件就不会执行
  • 如果执行callback,并且传入err,那么就会直接执行callback,并且后续回调事件不会执行
  • 如果执行callback,没有传入err,那么回调事件就会就行执行,直到结束才执行callback方法
  1. 触发回调事件

触发事件监听器时,接收的实参与实例化时注册的形参相对应,并且在最后传入一个callback事件;

  • 通过callAsync触发
this.hook.coffeeHook.callAsync(name, remark, (err, data) => {
    if (err) {
        console.log('err::::::::', err);
    } else {
        console.log('data::::::::', data);
    }
});
复制代码
  • 通过promise触发
this.hook.coffeeHook
  .promise(name, remark)
  .then((data) => {
    console.log('data::::::::', data);
  })
  .catch(err => {
    console.log('err::::::::', err);
  });
复制代码
  1. 其它同步方法

AsyncSeriesBailHook:只要当前执行的事件返回值不是undefined,那么后续的事件都不会执行

AsyncSeriesWaterfallHook:如果当前执行的事件返回的值不为undefined,那么下一个事件的第一个参数替换为该返回值

EventEmitter

提供一下EventEmitter实现的事件驱动的代码,仅供了解。

const EventEmitter = require('events');

const emitter = new EventEmitter();

emitter.on('coffee', function grindListener(name) {
  console.log(`grind ${name}`);
});
// 第二个监听器
emitter.on('coffee', function secondListener(name, remark) {
  console.log(`grind ${name}, ${remark}`);
});
// 第三个监听器
emitter.on('coffee', function garlandListener() {
  console.log(`garland`);
});

emitter.emit('coffee', 'Blue Mountain Coffee', 'use suga');
复制代码

demo地址

分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改