乐趣区

浅析webpack源码之Tapable粗解(五)

打开 compile
class Compiler extends Tapable {
constructor(context) {
super();
this.hooks = {
//…
}
}
}
Compiler 是个构造函数,定义了一些静态属性和方法
我们先看 Tapable
Tapable 在 node_modules 插件下 git 地址上面写的解释 Just a little module for plugins 就跟没写一样在 lib/index.js 文件下
exports.__esModule = true;
exports.Tapable = require(“./Tapable”);
exports.SyncHook = require(“./SyncHook”);
exports.SyncBailHook = require(“./SyncBailHook”);
exports.SyncWaterfallHook = require(“./SyncWaterfallHook”);
exports.SyncLoopHook = require(“./SyncLoopHook”);
exports.AsyncParallelHook = require(“./AsyncParallelHook”);
exports.AsyncParallelBailHook = require(“./AsyncParallelBailHook”);
exports.AsyncSeriesHook = require(“./AsyncSeriesHook”);
exports.AsyncSeriesBailHook = require(“./AsyncSeriesBailHook”);
exports.AsyncSeriesWaterfallHook = require(“./AsyncSeriesWaterfallHook”);
exports.HookMap = require(“./HookMap”);
exports.MultiHook = require(“./MultiHook”);

我们看到输出的一些对象方法每一个对应一个模块
而在 webpakck Compiler.js 下引入的下面
const {
Tapable,
SyncHook,
SyncBailHook,
AsyncParallelHook,
AsyncSeriesHook
} = require(“tapable”);
so,我们先研究引入的对象
tap 的英文单词解释,除了最常用的 点击 手势之外,还有一个意思是 水龙头 进一步可以理解为 tapable 是管理事件流的意思借用网上的一张图,tapable 是事件管家之所以名称有差距,是版本问题导致的,目前的是 1.1.0 版本,我们看看 git 果然证明了 v0.2.8 以前的全部都是下图函数所示???? 看个源码好艰辛我们还是看一看 git 上的 readme.md 文档吧
class Car {
constructor() {
this.hooks = {
accelerate: new SyncHook([“newSpeed”]),
break: new SyncHook(),
calculateRoutes: new AsyncParallelHook([“source”, “target”, “routesList”])
};
}

/* … */
}

const myCar = new Car();

// 可以理解为 trigger 触发,consument 是消费者的意思
// Use the tap method to add a consument
myCar.hooks.break.tap(“WarningLampPlugin”, () => warningLamp.on());

myCar.hooks.accelerate.tap(“LoggerPlugin”, newSpeed => console.log(`Accelerating to ${newSpeed}`));

// 异步方法
myCar.hooks.calculateRoutes.tapAsync(“BingMapsPlugin”, (source, target, routesList, callback) => {
bing.findRoute(source, target, (err, route) => {
if(err) return callback(err);
routesList.add(route);
// call the callback
callback();
});
});
// 异步方法
// You can still use sync plugins
myCar.hooks.calculateRoutes.tap(“CachedRoutesPlugin”, (source, target, routesList) => {
const cachedRoute = cache.get(source, target);
if(cachedRoute)
routesList.add(cachedRoute);
})

上面写的 tap 对应如下的 call 方法
class Car {
/* … */

setSpeed(newSpeed) {
this.hooks.accelerate.call(newSpeed);
}

useNavigationSystemPromise(source, target) {
const routesList = new List();
return this.hooks.calculateRoutes.promise(source, target, routesList).then(() => {
return routesList.getRoutes();
});
}

useNavigationSystemAsync(source, target, callback) {
const routesList = new List();
this.hooks.calculateRoutes.callAsync(source, target, routesList, err => {
if(err) return callback(err);
callback(null, routesList.getRoutes());
});
}
}
很像监听者模式,当你 call 是执行你 on 的函数,也就是按照这个文档所说的 running your plugins
这就是这个插件所做的,当然这个插件还做了异步处理等一些其他的事情之所以用这个插件,是因为能很好的处理时间流异步回调的情况
具体的 Tapable 如何实现,很复杂
webpack 很多核心功能本身是以插件的形式开发的
参考文章
Webpack 源码(一)—— Tapable 和 事件流浅析 webpack 源码之 Tapable 介绍 git 文档 Tapable 中文文档 webpack 源码分析(一)— Tapable 插件架构探索 webpack 源码(三)— Tapable 插件使用 2018-04-10
话说看了这么多,感觉还是很凌乱,Tapable 到底是怎么做的,以及优点在哪里,我们往下读,也许读着读着就会豁然开朗,所以标题就从 Tapable 详解到粗解

退出移动版