阅读 283

webpack核心模块-tapable

简介

在看L7源码时,看到我们使用了webpack的核心模块tapable,这个模块是连接webpack各个plugin的关键纽带,简单的说,如果不了解tapable,那肯定看不懂webpack的源码,对于各个plugin的调用机制也不会清楚。

总览

tapable我们可以简单的把它理解成webpack中的EventEmitter,用来发布和订阅消息。它暴露了很多hook,这些hook都是为自定义的plugin挂载提供的: ### 基础用法 可以知道tapable分为同步和异步两种hook,要注意的是异步hook分为并行和串行,而同步hook自然都是串行的。 先了解一下注册和触发的方法:

同步Hook异步Hook
注册taptapAsync,tapPromise,tap
触发callcallAsync,promise

最好在Class的构造函数中使用new初始化Hook:接受一个数组参数,触发方法会根据传参,接受同样数量的参数。

class Kunkun{
  constructor() {
    this.hooks = {
      sing: new SyncHook(["song"]),
      dance: new SyncWaterfallHook(['danceName']),
      rap: new SyncBailHook(),
      basketball: AsyncSeriesHook(["shot"]),
    };
  }
}
const kunkun = new Kunkun();

// 注册方法
// 有参数
kunkun.hooks.sing.tap("GoSing", song => {console.log(`唱了一首${song}`)});
// 无参数
kunkun.hooks.dance.tap("GoDance", danceName => {console.log(`跳了一支${danceName}`)});
kunkun.hooks.rap.tap("GoRap", () => {console.log('来了一首Rap')});
// 注册异步函数
kunkun.hooks.basketball.tapPromise("GoBasketball", (shot, callback) => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(`投了一个${shot}分球`);
    },1000)
  })
});

//执行同步Hook
kunkun.hooks.sing.call('鸡你太美');
kunkun.hooks.dance.call('华尔兹');
kunkun.hooks.rap.call();
//执行异步Hook
myCar.hooks.basketball.promise(2).then((res) => {
	console.log(res)
})

复制代码

Hook详细场景

SyncHook

在触发事件之后,会按照事件注册的先后顺序执行所有的事件处理函数。

SyncBailHook

如果事件处理函数执行时有一个返回值不为空,则跳过剩下未执行的事件处理函数,还依照上述例子:

kunkun.hooks.rap.tap("GoRap1", () => {console.log('来了一首Rap1')});
kunkun.hooks.rap.tap("GoRap2", () => {
  console.log('来了一首Rap2');
  return 0;
});
kunkun.hooks.rap.tap("GoRap3", () => {console.log('来了一首Rap3')});

kunkun.hooks.rap.call();

// 来了一首Rap1
// 来了一首Rap2
复制代码

SyncWaterfallHook

上一个事件处理函数的返回值作为参数传递给下一个事件处理函数,只有第一个绑定的函数里的参数是触发时的实际参数,其他绑定方法里的参数都是上一个的结果:

kunkun.hooks.dance.tap("Dance1", danceName => {
  return `跳了一支${danceName}1`
});
kunkun.hooks.dance.tap("Dance2", (data) => {
	return `跳了一支${danceName}2`
});
kunkun.hooks.dance.tap("Dance3", (data) => {
  return `跳了一支${danceName}3`
});
kunkun.hooks.dance.call('华尔兹');

//跳了一支华尔兹123
复制代码

SyncLoopHook

事件处理函数返回 true表示循环执行当前事件处理函数,返回 undefined表示结束循环, 和SyncBailHook不同,SyncBailHook 只决定是否继续向下执行后面的事件处理函数,而 SyncLoopHook 的循环是指循环执行每一个事件处理函数,直到返回 undefined 为止,才会继续向下执行其他事件处理函数,执行机制同理。

let syncLoopHook = new SyncLoopHook(["name", "age"]);
let temp = 0;
// 注册事件
syncLoopHook.tap("1", name => {
    console.log("1", name, temp);
    return temp++ < 2 ? true : undefined;
});
syncLoopHook.tap("2", name => {
    console.log("2", name, temp);
    return temp++ < 5 ? true : undefined;
});
syncLoopHook.tap("3", name => console.log("3", name));

syncLoopHook.call("maosong");

// 1 maosong 0
// 1 maosong 1
// 1 maosong 2
// 2 maosong 3
// 2 maosong 4
// 2 maosong 5
// 3 maosong
复制代码

AsyncParallelHook

所有注册的方法同步并行,和Promise.all()一样

AsyncSeriesHook

所有注册的方法串行,也就是一个方法执行完,有了结果之后再执行下一个

AsyncParallelBailHook

和SyncBailHook一个思想,只不过加入了异步

AsyncSeriesBailHook

和SyncBailHook一个思想,只不过加入了异步

AsyncSeriesWaterfallHook

AsyncSeriesHook + SyncWaterfallHook  详情去查看这俩Hook

异步注册和触发两种方式的区别

tapAsync / callAsync

在AsyncParallelHook中,注册时提供done()方法来表明异步结束,触发时可以传入callback来在所有注册的方法都运行结束后触发回调

const { AsyncParallelHook } = require("tapable");
let asyncParallelHook = new AsyncParallelHook(["name"]);
asyncParallelHook.tapAsync("1", (name, done) => {
  settimeout(() => {
    console.log("1", name);
    done();
  }, 1000);
});
asyncParallelHook.tapAsync("2", (name, done) => {
  settimeout(() => {
    console.log("2", name);
    done();
  }, 1000);
});
// 触发事件,让监听函数执行
asyncParallelHook.callAsync("maosong", () => {
    console.log("over");
});

// 1 maosong
// 2 maosong
// over

复制代码

在AsyncSeriesHook中提供next()方法来进入下一个注册的监控方法,用法和上面一样。

done()和next()的区别?

done 方法只为了检测是否已经满足条件执行 callAsync 的回调,如果中间某个事件处理函数没有调用 done,只是不会调用 callAsync 的回调,但是所有的事件处理函数都会执行。

next 执行机制更像 Generator中的next(),在注册事件的回调中如果不调用 next,则在触发事件时会在没有调用 next 的事件处理函数的位置 “卡死”,即不会继续执行后面的事件处理函数,只有都调用 next 才能继续,而最后一个事件处理函数中调用 next 决定是否调用 callAsync 的回调。

tapPromise /promise

具体示例看一开始的kunkun示例就好,需要返回一个promise实例

文章分类
前端
文章标签