由浅入深学习Tapable

文章目录

由浅入深学习Tapable

webpack有两个非常重要的类:Compiler和Compilation。他们通过注入插件的方式,来监听webpack的所有生命周期,插件的注入离不开各种各样的Hook,而他们的Hook是如何得到的呢?其实是创建了Tapable库中各种Hook的实例。

Tapable是什么

Tapable 是一个任务调度库,它的核心思基于发布订阅模式 是将任务执行逻辑和调度逻辑分离,Tapable在webpack中用于plugin的管理,在可以实现复杂调度逻辑的同时尽可能保证可维护性。

Tapable 的机制与Event类似,它可以用来定义各种各样的钩子,相当于Event中的事件注册,但是与Event不同的是,Event中各个事件之间相互不关联,互不影响,但是tapable注册的事件之间可以是有关系的,这种关系通过Tapable 定义的各个钩子类来实现。其实,Tapable的核心就是这些钩子类。因此,我们接下来的重点就是讲述这些钩子类,并实现它们。

Tapable的Hook分类

同步和异步的

  • 以sync开头的,是同步的Hook
  • 以async开头的,两个事件处理回调,不会等待上一次处理回调结束后再执行下一次回调

使用

Tapable的每个子类都是一个用于注册和触发事件的钩子,我们可以查看一下SyncHook实例身上有哪些属性,找到它注册事件和触发事件的属性。

js 复制代码
SyncHook {
  _args: [],
  name: undefined,
  taps: [],
  interceptors: [],
  _call: [Function: CALL_DELEGATE],
  call: [Function: CALL_DELEGATE],   // 用于触发同步事件的钩子
  _callAsync: [Function: CALL_ASYNC_DELEGATE],
  callAsync: [Function: CALL_ASYNC_DELEGATE],  // 用于触发异步事件的钩子
  _promise: [Function: PROMISE_DELEGATE],
  promise: [Function: PROMISE_DELEGATE],
  _x: undefined,
  compile: [Function: COMPILE],
  tap: [Function: tap],        // 用于注册同步事件的钩子
  tapAsync: [Function: TAP_ASYNC],  // 用于注册异步事件的钩子
  tapPromise: [Function: TAP_PROMISE],
  constructor: [Function: SyncHook] 
}

Sync*同步类型钩子

基本使用

js 复制代码
const { SyncHook } = require("tapable");

class myCompiler {
  constructor() {
    this.hooks = {
        // 1. 创建hooks(webpack完成)
      syncHook: new SyncHook(["name", "age"]),
    };

    // 2. 用hooks监听事件(自定义plugin)
    this.hooks.syncHook.tap("event1", (name, age) => {
      console.log("event1事件监听执行了:", name, age);
    });

    this.hooks.syncHook.tap("event2", (name, age) => {
      console.log("event2事件监听执行了:", name, age);
    });
  }
}

const compiler = new myCompiler();
// 3. 发出去事件(webpack完成)
compiler.hooks.syncHook.call("小张", 20);

结果:

bail

当有返回值时,就不会执行后续的事件触发了

js 复制代码
const { SyncBailHook } = require("tapable");

class myCompiler {
  constructor() {
    this.hooks = {
      // 1. 创建hooks
      bailHook: new SyncBailHook(["name", "age"]),
    };

    // 2. 用hooks监听事件(自定义plugin)
    this.hooks.bailHook.tap("event1", (name, age) => {
      console.log("event1事件监听执行了:", name, age);
      return 123
    });

    this.hooks.bailHook.tap("event2", (name, age) => {
      console.log("event2事件监听执行了:", name, age);
    });
  }
}

const compiler = new myCompiler();
// 3. 发出去事件
compiler.hooks.bailHook.call("小张", 20);

输出结果:

Loop

当返回值为true,就会反复执行该事件,当返回值为undefined或者不返回内容,就退出事件

js 复制代码
const { SyncLoopHook } = require("tapable");

class myCompiler {
  constructor() {
    this.hooks = {
      // 1. 创建hooks
      loopHook: new SyncLoopHook(["name", "age"]),
    };

    let count = 0;

    // 2. 用hooks监听事件(自定义plugin)
    this.hooks.loopHook.tap("event1", (name, age) => {
      if (count < 5) {
        console.log("event1事件监听执行了:", name, age);
        count++;
        return true;
      } else {
        return;
      }
    });

    this.hooks.loopHook.tap("event2", (name, age) => {
      console.log("event2事件监听执行了:", name, age);
    });
  }
}

const compiler = new myCompiler();
// 3. 发出去事件
compiler.hooks.loopHook.call("小张", 20);

输出结果:

Waterfall

当返回值不为undefined时,会将这次返回的结果作为下次事件的第一个参数

js 复制代码
const { SyncWaterfallHook } = require("tapable");

class myCompiler {
  constructor() {
    this.hooks = {
      // 1. 创建hooks
      waterfallHook: new SyncWaterfallHook(["name", "age"]),
    };

    // 2. 用hooks监听事件(自定义plugin)
    this.hooks.waterfallHook.tap("event1", (name, age) => {
      console.log("event1事件监听执行了:", name, age);
      // return "小李",小李作为下一个事件的第一个参数
      return "小李";
    });

    this.hooks.waterfallHook.tap("event2", (name, age) => {
      console.log("event2事件监听执行了:", name, age);
    });
  }
}

const compiler = new myCompiler();
// 3. 发出去事件
compiler.hooks.waterfallHook.call("小张", 20);

输出结果:

Async*异步类型钩子

Parallel

并行,不会等到上一个事件回调执行结束,才会执行下一次事件处理回调

js 复制代码
const { AsyncParallelHook } = require("tapable");

class myCompiler {
  constructor() {
    this.hooks = {
      // 1. 创建hooks
      parallelHook: new AsyncParallelHook(["name", "age"]),
    };

    // 2. 用hooks监听事件(自定义plugin)
    this.hooks.parallelHook.tapAsync("event1", (name, age) => {
      setTimeout(() => {
        console.log("event1事件监听执行了:", name, age);
      }, 3000);
    });

    this.hooks.parallelHook.tapAsync("event2", (name, age) => {
      setTimeout(() => {
        console.log("event2事件监听执行了:", name, age);
      }, 3000);
    });
  }
}

const compiler = new myCompiler();
// 3. 发出去事件
compiler.hooks.parallelHook.callAsync("小张", 20);

输出结果:

Series

串行,会等待上一次异步的Hook,即按照注册的顺序依次执行

js 复制代码
const { AsyncSeriesHook } = require("tapable");

class myCompiler {
  constructor() {
    this.hooks = {
      // 1. 创建hooks
      seriesHook: new AsyncSeriesHook(["name", "age"]),
    };

    // 2. 用hooks监听事件(自定义plugin)
    this.hooks.seriesHook.tapAsync("event1", (name, age, callback) => {
      console.log("event1事件监听执行了:", name, age);
      // 这个callback决定下一个事件的执行
      callback();
    });

    this.hooks.seriesHook.tapAsync("event2", (name, age, callback) => {
      console.log("event2事件监听执行了:", name, age);
      // 这个callback决定发出事件中的函数执行
      callback();
    });
  }
}

const compiler = new myCompiler();
// 3. 发出去事件
// 第三个参数决定第一个事件的执行
compiler.hooks.seriesHook.callAsync("小张", 20, () => {
  console.log("所有事件都执行完成啦!");
});

输出结果:

相关推荐
福兮说3 小时前
设计稿是 #4A7C6F,页面量出来是 #4B7C6F:HEX、HSL、透明度、canvas 来回转的七个坑
前端·javascript·css·canvas
郑州光合科技余经理4 小时前
海外版外卖加盟:总站与分站配送规则怎么分开管
java·开发语言·前端·后端·uni-app·php·ai编程
小呆呆6665 小时前
副业搞起来,小说,漫画,漫剧的成本优化思路
前端·后端·面试
凤城老人5 小时前
从 PyQt6 到 Electron:给 Edge TTS 做一个“多角色配音机“的踩坑手记
javascript·typescript·electron
Dovis(誓平步青云)5 小时前
浇水提醒刚弹出又消失,植物状态别只存一个百分比
开发语言·前端·javascript·pdf·ecmascript·电脑
码艺-Alimjan5 小时前
Vben Admin 新增维吾尔语 Vben-Modal的关键坑之一
前端·javascript·vue.js
可乐鸡翅yeah_6 小时前
hls.js 手动自定义 http 请求 loader,修改请求头实战
开发语言·前端·javascript·网络协议·http·ecmascript·m3u8在线
IT_陈寒6 小时前
Vite静态资源导入这个坑我帮你们踩过了
前端·人工智能·后端
广州华水科技6 小时前
大坝安全监测解决方案:单北斗GNSS形变监测系统应用与维护
前端
yivifu7 小时前
中文古籍电子书注释集成
前端·javascript·python·beautifulsoup·epub