webpack核心模块tapable用法解析

前不久写了一篇webpack基本原理和AST用法的文章,本来想接着写webpack plugin的原理的,但是发现webpack plugin高度依赖tapable这个库,不清楚tapable而直接去看webpack plugin始终有点雾里看花的意思。所以就先去看了下tapable的文档和源码,发现这个库非常有意思,是增强版的发布订阅模式。发布订阅模式在源码世界实在是太常见了,我们已经在多个库源码里面见过了:

redux的subscribe和dispatch

Node.js的EventEmitter

redux-saga的take和put

这些库基本都自己实现了自己的发布订阅模式,实现方式主要是用来满足自己的业务需求,而tapable并没有具体的业务逻辑,是一个专门用来实现事件订阅或者他自己称为hook(钩子)的工具库,其根本原理还是发布订阅模式,但是他实现了多种形式的发布订阅模式,还包含了多种形式的流程控制。

tapable暴露多个API,提供了多种流程控制方式,连使用都是比较复杂的,所以我想分两篇文章来写他的原理:

先看看用法,体验下他的多种流程控制方式

通过用法去看看源码是怎么实现的

本文就是讲用法的文章,知道了他的用法,大家以后如果有自己实现hook或者事件监听的需求,可以直接拿过来用,非常强大!

本文例子已经全部上传到GitHub,大家可以拿下来做个参考:https://github.com/dennis-jiang/Front-End-Knowledges/tree/master/Examples/Engineering/tapable-usage

tapable是什么

tapable是webpack的核心模块,也是webpack团队维护的,是webpack plugin的基本实现方式。他的主要功能是为使用者提供强大的hook机制,webpack plugin就是基于hook的。

主要API

下面是官方文档中列出来的主要API,所有API的名字都是以Hook结尾的:

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

这些API的名字其实就解释了他的作用,注意这些关键字:Sync, Async, Bail, Waterfall, Loop, Parallel, Series。下面分别来解释下这些关键字:

Sync:这是一个同步的hook

Async:这是一个异步的hook

Bail:Bail在英文中的意思是保险,保障的意思,实现的效果是,当一个hook注册了多个回调方法,任意一个回调方法返回了不为undefined的值,就不再执行后面的回调方法了,就起到了一个“保险丝”的作用。

Waterfall:Waterfall在英语中是瀑布的意思,在编程世界中表示顺序执行各种任务,在这里实现的效果是,当一个hook注册了多个回调方法,前一个回调执行完了才会执行下一个回调,而前一个回调的执行结果会作为参数传给下一个回调函数。

Loop:Loop就是循环的意思,实现的效果是,当一个hook注册了回调方法,如果这个回调方法返回了true就重复循环这个回调,只有当这个回调返回undefined才执行下一个回调。

Parallel:Parallel是并行的意思,有点类似于Promise.all,就是当一个hook注册了多个回调方法,这些回调同时开始并行执行。

Series:Series就是串行的意思,就是当一个hook注册了多个回调方法,前一个执行完了才会执行下一个。

Parallel和Series的概念只存在于异步的hook中,因为同步hook全部是串行的。

下面我们分别来介绍下每个API的用法和效果。

同步API

同步API就是这几个:

const { SyncHook, SyncBailHook, SyncWaterfallHook, SyncLoopHook, } = require("tapable");

前面说了,同步API全部是串行的,所以这几个的区别就在流程控制上。

SyncHook

SyncHook是一个最基础的hook,其使用方法和效果接近我们经常使用的发布订阅模式,注意tapable导出的所有hook都是类,基本用法是这样的:

const hook = new SyncHook(["arg1", "arg2", "arg3"]);

因为SyncHook是一个类,所以使用new来生成一个实例,构造函数接收的参数是一个数组["arg1", "arg2", "arg3"],这个数组有三项,表示生成的这个实例注册回调的时候接收三个参数。实例hook主要有两个实例方法:

tap:就是注册事件回调的方法。

call:就是触发事件,执行回调的方法。

下面我们扩展下官方文档中小汽车加速的例子来说明下具体用法:

const { SyncHook } = require("tapable"); // 实例化一个加速的hook const accelerate = new SyncHook(["newSpeed"]); // 注册第一个回调,加速时记录下当前速度 accelerate.tap("LoggerPlugin", (newSpeed) => console.log("LoggerPlugin", `加速到${newSpeed}`) ); // 再注册一个回调,用来检测是否超速 accelerate.tap("OverspeedPlugin", (newSpeed) => { if (newSpeed > 120) { console.log("OverspeedPlugin", "您已超速!!"); } }); // 再注册一个回调,用来检测速度是否快到损坏车子了 accelerate.tap("DamagePlugin", (newSpeed) => { if (newSpeed > 300) { console.log("DamagePlugin", "速度实在太快,车子快散架了。。。"); } }); // 触发一下加速事件,看看效果吧 accelerate.call(500);

然后运行下看看吧,当加速事件出现的时候,会依次执行这三个回调:

image-20210309160302799

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsfpzg.html