由浅入深学习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("所有事件都执行完成啦!");
});

输出结果:

相关推荐
Boilermaker199241 分钟前
【Java EE】SpringIoC
前端·数据库·spring
中微子1 小时前
JavaScript 防抖与节流:从原理到实践的完整指南
前端·javascript
天天向上10241 小时前
Vue 配置打包后可编辑的变量
前端·javascript·vue.js
芬兰y1 小时前
VUE 带有搜索功能的穿梭框(简单demo)
前端·javascript·vue.js
好果不榨汁1 小时前
qiankun 路由选择不同模式如何书写不同的配置
前端·vue.js
小蜜蜂dry1 小时前
Fetch 笔记
前端·javascript
拾光拾趣录2 小时前
列表分页中的快速翻页竞态问题
前端·javascript
小old弟2 小时前
vue3,你看setup设计详解,也是个人才
前端
Lefan2 小时前
一文了解什么是Dart
前端·flutter·dart
Patrick_Wilson2 小时前
青苔漫染待客迟
前端·设计模式·架构