webpack原理篇(五十三):Tapable插件架构与Hooks设计

Posted 凯小默

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了webpack原理篇(五十三):Tapable插件架构与Hooks设计相关的知识,希望对你有一定的参考价值。

说明

玩转 webpack 学习笔记

compiler

上一节里面有个重要的东西就是 compiler

下面找一下这个东东,在 my-project\\node_modules\\webpack\\lib\\webpack.js 里面可以看到


这里的 Compiler 跟 MultiCompiler 分别引用了不同的 js

const Compiler = require("./Compiler");
const MultiCompiler = require("./MultiCompiler");

找到 my-project\\node_modules\\webpack\\lib\\Compiler.js


可以看到 Compiler 继承了 Tapable

Webpack 的本质

Webpack 可以将其理解是一种基于事件流的编程范例,一系列的插件运行。

核心对象 Compiler 继承 Tapable:

class Compiler extends Tapable 
	// ... 

核心对象 Compilation 继承 Tapable

class Compilation extends Tapable 
	// ... 

Tapable 是什么?

Tapable 是一个类似于 Node.js 的 EventEmitter 的库, 主要是控制钩子函数的发布与订阅,控制着 webpack 的插件系统。

Tapable 库暴露了很多 Hook(钩子)类,为插件提供挂载的钩子

const 
	SyncHook, // 同步钩子
	SyncBailHook, // 同步熔断钩子(遇到 return 直接返回)
	SyncWaterfallHook, // 同步流水钩子(结果可以传递给下一个插件)
	SyncLoopHook, // 同步循环钩子
	AsyncParallelHook, // 异步并发钩子
	AsyncParallelBailHook, // 异步并发熔断钩子
	AsyncSeriesHook, // 异步串行钩子
	AsyncSeriesBailHook, // 异步串行熔断钩子
	AsyncSeriesWaterfallHook // 异步串行流水钩子
 = require("tapable");

Tapable hooks 类型

typefunction
Hook所有钩子的后缀
Waterfall同步方法,但是它会传值给下一个函数
Bail熔断:当函数有任何返回值,就会在当前执行函数停止
Loop监听函数返回true表示继续循环,返回undefine表示结束循环
Sync同步方法
AsyncSeries异步串行钩子
AsyncParallel异步并行执行钩子

Tapable 的使用:new Hook 新建钩子

Tapable 暴露出来的都是类方法,new 一个类方法获得我们需要的钩子

class 接受数组参数 options ,非必传。类方法会根据传参,接受同样数量的参数。

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

Tapable 的使用:钩子的绑定与执行

Tabpack 提供了同步 & 异步绑定钩子的方法,并且他们都有绑定事件和执行事件对应的方法。

Async*Sync*
绑定: tapAsync/tapPromise/tap绑定: tap
执行: callAsync/promise执行: call

Tapable 的使用:hook 基本用法示例

const hook1 = new SyncHook(["arg1", "arg2", "arg3"]);
// 绑定事件到webapck事件流
hook1.tap('hook1', (arg1, arg2, arg3) => console.log(arg1, arg2, arg3)) //1,2,3
// 执行绑定的事件
hook1.call(1,2,3)

Tapable 的使用:实际例子演示

新建项目

1、我们先新建一个项目 tapable-kaimo,然后执行下面命令

npm init -y
npm i tapable

先添加一个 index.js 文件,输入下面代码

const 
    SyncHook
 = require('tapable');

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

hook.tap("hook1", (arg1, arg2, arg3) => 
    console.log(arg1, arg2, arg3);
);

hook.call(1, 2, 3);

运行 node index.js 结果如下

实现下面例子

定义一个 Car 方法,在内部 hooks 上新建钩子。分别是同步钩子 accelerate、brake( accelerate 接受一个参数)、异步钩子 calculateRoutes。

使用钩子对应的绑定和执行方法

calculateRoutes 使用 tapPromise 可以返回一个 promise 对象

新建一个 car.js 文件,添加下面代码

const 
    SyncHook,
    AsyncSeriesHook
 = require('tapable');

// 创建 Car 类
class Car 
    constructor() 
        this.hooks = 
            accelerate: new SyncHook(['newSpeed']), // 加速 hook
            brake: new SyncHook(), // 刹车 hook
            calculateRoutes: new AsyncSeriesHook(['source', 'target', 'routesList']) // 计算路径 hook
        
    


// 实例化 Car
const kaimoCar = new Car();

// 绑定同步钩子
kaimoCar.hooks.brake.tap('WarningLampPlugin', () => 
    console.log('WarningLampPlugin');
)

// 绑定同步钩子 并传参
kaimoCar.hooks.accelerate.tap('LoggerPlugin', newSpeed => 
    console.log(`Accelerate to $newSpeed`);
)

// 绑定一个异步 Promise 钩子
kaimoCar.hooks.calculateRoutes.tapPromise('calculateRoutes tapPromise', (source, target, routesList, callback) => 
    return new Promise((resolve, reject) => 
        setTimeout(() => 
            console.log(`tapPromise to $source $target $routesList`);
            resolve()
        , 1000)
    )
)

/*****************下面开始执行***************/ 

kaimoCar.hooks.brake.call();
kaimoCar.hooks.accelerate.call(313);

console.time('kaimoCar cost');

kaimoCar.hooks.calculateRoutes.promise('Async', 'hook', 'kaimo demo').then(() => 
    console.timeEnd('kaimoCar cost');
, err => 
    console.error(err);
    console.timeEnd('kaimoCar cost');
)

运行命令执行 node car.js,结果如下

以上是关于webpack原理篇(五十三):Tapable插件架构与Hooks设计的主要内容,如果未能解决你的问题,请参考以下文章

webpack原理:Tapable源码分析及钩子函数作用分析

webpack原理篇(六十三):插件基本结构介绍

Webpack Tapable原理详解

webpack之tapable

webpack之tapable

webpack4.0各个击破—— tapable篇