起因

搜索引擎搜索tapable中文文档,你会看见各种翻译,点进去一看,确实是官方的文档翻译过来的,但是webpack的文档确实还有很多需要改进的地方,既然是开源的为什么不去github上的tapable库看呢,一看,确实,比webpack文档上的描述得清楚得多.

tapable 是一个类似于nodejs 的EventEmitter 的库, 主要是控制钩子函数的发布与订阅,控制着webpack的插件系.webpack的本质就是一系列的插件运行.

Tapable

Tapable库 提供了很多的钩子类, 这些类可以为插件创建钩子


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

安装


npm install --save tapable

使用

所有的钩子构造函数,都接受一个可选的参数,(这个参数最好是数组,不是tapable内部也把他变成数组),这是一个参数的字符串名字列表


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

最好的实践就是把所有的钩子暴露在一个类的hooks属性里面:


class Car {
constructor() {
this.hooks = {
accelerate: new SyncHook(["newSpeed"]),
brake: new SyncHook(),
calculateRoutes: new AsyncParallelHook(["source", "target", "routesList"])
};
} /* ... */
}

其他开发者现在可以这样用这些钩子


const myCar = new Car(); // Use the tap method to add a consument
// 使用tap 方法添加一个消费者,(生产者消费者模式)
myCar.hooks.brake.tap("WarningLampPlugin", () => warningLamp.on());

这需要你传一个名字去标记这个插件:

你可以接收参数


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

在同步钩子中, tap 是唯一的绑定方法,异步钩子通常支持异步插件


// promise: 绑定promise钩子的API
myCar.hooks.calculateRoutes.tapPromise("GoogleMapsPlugin", (source, target, routesList) => {
// return a promise
return google.maps.findRoute(source, target).then(route => {
routesList.add(route);
});
});
// tapAsync:绑定异步钩子的API
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
// tap: 绑定同步钩子的API
myCar.hooks.calculateRoutes.tap("CachedRoutesPlugin", (source, target, routesList) => {
const cachedRoute = cache.get(source, target);
if(cachedRoute)
routesList.add(cachedRoute);
})

类需要调用被声明的那些钩子


class Car {
/* ... */ setSpeed(newSpeed) {
// call(xx) 传参调用同步钩子的API
this.hooks.accelerate.call(newSpeed);
} useNavigationSystemPromise(source, target) {
const routesList = new List();
// 调用promise钩子(钩子返回一个promise)的API
return this.hooks.calculateRoutes.promise(source, target, routesList).then(() => {
return routesList.getRoutes();
});
} useNavigationSystemAsync(source, target, callback) {
const routesList = new List();
// 调用异步钩子API
this.hooks.calculateRoutes.callAsync(source, target, routesList, err => {
if(err) return callback(err);
callback(null, routesList.getRoutes());
});
}
}

tapable会用最有效率的方式去编译(构建)一个运行你的插件的方法,他生成的代码依赖于一下几点:

  • 你注册的插件的个数.
  • 你注册插件的类型.
  • 你使用的调用方法(call, promise, async) // 其实这个类型已经包括了
  • 钩子参数的个数 // 就是你new xxxHook(['ooo']) 传入的参数
  • 是否应用了拦截器(拦截器下面有讲)

这些确定了尽可能快的执行.

钩子类型

每一个钩子都可以tap 一个或者多个函数, 他们如何运行,取决于他们的钩子类型

  • 基本的钩子, (钩子类名没有waterfall, Bail, 或者 Loop 的 ), 这个钩子只会简单的调用每个tap进去的函数
  • Waterfall, 一个waterfall 钩子,也会调用每个tap进去的函数,不同的是,他会从每一个函数传一个返回的值到下一个函数
  • Bail, Bail 钩子允许更早的退出,当任何一个tap进去的函数,返回任何值, bail类会停止执行其他的函数执行.(类似 Promise.race())
  • Loop, TODO(我.... 这里也没描述,应该是写文档得时候 还没想好这个要怎么写,我尝试看他代码去补全,不过可能需要点时间.)

此外,钩子可以是同步的,也可以是异步的,Sync, AsyncSeries 和 AsyncParallel ,从名字就可以看出,哪些是可以绑定异步函数的

  • Sync, 一个同步钩子只能tap同步函数, 不然会报错.
  • AsyncSeries, 一个 async-series 钩子 可以tap 同步钩子, 基于回调的钩子(我估计是类似chunk的东西)和一个基于promise的钩子(使用myHook.tap(), myHook.tapAsync()myHook.tapPromise().).他会按顺序的调用每个方法.
  • AsyncParallel, 一个 async-parallel 钩子跟上面的 async-series 一样 不同的是他会把异步钩子并行执行(并行执行就是把异步钩子全部一起开启,不按顺序执行).

拦截器(interception)

所有钩子都提供额外的拦截器API


// 注册一个拦截器
myCar.hooks.calculateRoutes.intercept({
call: (source, target, routesList) => {
console.log("Starting to calculate routes");
},
register: (tapInfo) => {
// tapInfo = { type: "promise", name: "GoogleMapsPlugin", fn: ... }
console.log(`${tapInfo.name} is doing its job`);
return tapInfo; // may return a new tapInfo object
}
})

call:(...args) => void当你的钩子触发之前,(就是call()之前),就会触发这个函数,你可以访问钩子的参数.多个钩子执行一次

tap: (tap: Tap) => void 每个钩子执行之前(多个钩子执行多个),就会触发这个函数

loop:(...args) => void 这个会为你的每一个循环钩子(LoopHook, 就是类型到Loop的)触发,具体什么时候没说

register:(tap: Tap) => Tap | undefined 每添加一个Tap都会触发 你interceptor上的register,你下一个拦截器的register 函数得到的参数 取决于你上一个register返回的值,所以你最好返回一个 tap 钩子.

Context(上下文)

插件和拦截器都可以选择加入一个可选的 context对象, 这个可以被用于传递随意的值到队列中的插件和拦截器.


myCar.hooks.accelerate.intercept({
context: true,
tap: (context, tapInfo) => {
// tapInfo = { type: "sync", name: "NoisePlugin", fn: ... }
console.log(`${tapInfo.name} is doing it's job`); // `context` starts as an empty object if at least one plugin uses `context: true`.
// 如果最少有一个插件使用 `context` 那么context 一开始是一个空的对象
// If no plugins use `context: true`, then `context` is undefined
// 如过tap进去的插件没有使用`context` 的 那么内部的`context` 一开始就是undefined
if (context) {
// Arbitrary properties can be added to `context`, which plugins can then access.
// 任意属性都可以添加到`context`, 插件可以访问到这些属性
context.hasMuffler = true;
}
}
}); myCar.hooks.accelerate.tap({
name: "NoisePlugin",
context: true
}, (context, newSpeed) => {
if (context && context.hasMuffler) {
console.log("Silence...");
} else {
console.log("Vroom!");
}
});

HookMap

一个 HookMap是一个Hooks映射的帮助类


const keyedHook = new HookMap(key => new SyncHook(["arg"]))

keyedHook.tap("some-key", "MyPlugin", (arg) => { /* ... */ });
keyedHook.tapAsync("some-key", "MyPlugin", (arg, callback) => { /* ... */ });
keyedHook.tapPromise("some-key", "MyPlugin", (arg) => { /* ... */ });

const hook = keyedHook.get("some-key");
if(hook !== undefined) {
hook.callAsync("arg", err => { /* ... */ });
}

钩子映射接口(HookMap interface)

Public(权限公开的):


interface Hook {
tap: (name: string | Tap, fn: (context?, ...args) => Result) => void,
tapAsync: (name: string | Tap, fn: (context?, ...args, callback: (err, result: Result) => void) => void) => void,
tapPromise: (name: string | Tap, fn: (context?, ...args) => Promise<Result>) => void,
intercept: (interceptor: HookInterceptor) => void
} interface HookInterceptor {
call: (context?, ...args) => void,
loop: (context?, ...args) => void,
tap: (context?, tap: Tap) => void,
register: (tap: Tap) => Tap,
context: boolean
} interface HookMap {
for: (key: any) => Hook,
tap: (key: any, name: string | Tap, fn: (context?, ...args) => Result) => void,
tapAsync: (key: any, name: string | Tap, fn: (context?, ...args, callback: (err, result: Result) => void) => void) => void,
tapPromise: (key: any, name: string | Tap, fn: (context?, ...args) => Promise<Result>) => void,
intercept: (interceptor: HookMapInterceptor) => void
} interface HookMapInterceptor {
factory: (key: any, hook: Hook) => Hook
} interface Tap {
name: string,
type: string
fn: Function,
stage: number,
context: boolean
}

Protected(保护的权限),只用于类包含的(里面的)钩子


interface Hook {
isUsed: () => boolean,
call: (...args) => Result,
promise: (...args) => Promise<Result>,
callAsync: (...args, callback: (err, result: Result) => void) => void,
} interface HookMap {
get: (key: any) => Hook | undefined,
for: (key: any) => Hook
}

MultiHook

把其他的Hook 重定向(转化)成为一个 MultiHook


const { MultiHook } = require("tapable"); this.hooks.allHooks = new MultiHook([this.hooks.hookA, this.hooks.hookB]);

OK 所有的内容我都已翻译完成.

其中有很多不是直译,这样写下来感觉就是按照原文的脉络重新写了一遍....,应该能更清楚明白,要不是怕丢脸我就给个原创了,哈哈.

之后, 我还会写一篇完整的原创解析,直击源码,搞定tapable, 完全了解webpack插件系统(webpack本来就是一个插件的事件流), 好久没写原创了. 我自己也很期待.

来源:https://segmentfault.com/a/1190000017420937

这才是官方的tapable中文文档的更多相关文章

  1. django2.0 官方中文文档地址

    django2.0 官方开始发布中文文档了,之前还想着一直翻译完成所有有必要的内容,想着可以省事一些了,打开以后看了一下,发现官方的中文文档还没翻译完成, 现在(2018-7-10)最新章节是是  编 ...

  2. PyTorch官方中文文档:torch.nn

    torch.nn Parameters class torch.nn.Parameter() 艾伯特(http://www.aibbt.com/)国内第一家人工智能门户,微信公众号:aibbtcom ...

  3. ReactNative官方中文文档0.21

    整理了一份ReactNative0.21中文文档,提供给需要的reactnative爱好者.ReactNative0.21中文文档.chm  百度盘下载:ReactNative0.21中文文档 来源: ...

  4. 学习Python 新去处:Python 官方中文文档

    Python 作为世界上最好用的语言,官方支持的文档一直没有中文.小伙伴们已经习惯了原汁原味的英文文档,但如果有官方中文文档,那么查阅或理解速度都会大大提升.本文将介绍隐藏在 Python 官网的中文 ...

  5. mysql 新手入门 官方文档+官方中文文档附地址

    点评: 官方文档地址 官方中文文档地址 sql语句扩展

  6. PyTorch官方中文文档:torch.optim 优化器参数

    内容预览: step(closure) 进行单次优化 (参数更新). 参数: closure (callable) –...~ 参数: params (iterable) – 待优化参数的iterab ...

  7. java中文文档官方下载

    一直在寻找它,今天无意之间终于发现它了! http://download.oracle.com/technetwork/java/javase/6/docs/zh/api/overview-summa ...

  8. PyTorch官方中文文档:PyTorch中文文档

    PyTorch中文文档 PyTorch是使用GPU和CPU优化的深度学习张量库. 说明 自动求导机制 CUDA语义 扩展PyTorch 多进程最佳实践 序列化语义 Package参考 torch to ...

  9. Keras官方中文文档:Keras安装和配置指南(Windows)

    这里需要说明一下,笔者不建议在Windows环境下进行深度学习的研究,一方面是因为Windows所对应的框架搭建的依赖过多,社区设定不完全:另一方面,Linux系统下对显卡支持.内存释放以及存储空间调 ...

随机推荐

  1. PyQt4发射信号

    继承自QtCore.QObject的对象均可以发射信号.如果我们单击一个按钮,那么一个clicked()信号就会被触发.下面的示例演示如何手动发射一个信号. #!/usr/bin/python # - ...

  2. x86 x64下调用约定浅析

    x86平台下调用约定 我们都知道x86平台下常用的有三种调用约定,__cdecl.__stdcall.__fastcall.我们分别对这三种调用约定进行分析. __cdecl __cdecl是C/C+ ...

  3. java基础---->数字签名算法的介绍

    数字签名(又称公钥数字签名)是一种类似写在纸上的普通的物理签名,但是使用了公钥加密领域的技术实现,用于鉴别数字信息的方法.关于数字签名的介绍,可以参见百度百科:http://baike.baidu.c ...

  4. SSL安装方法二:Windows Server 2008安装SSL证书(IIS 7.5)

    SSL证书CSR和CA证书唯一的区别就在:申请证书中的通用名称,具体还要看具体的项目这里只做参考.可以参考SSL安装方法一 背景:IIS 7.5 一.收到SSL证书 仔细阅读邮件 按步骤进行: 1)  ...

  5. 将web项目打成war包部署在tomcat步骤

    将web项目打成war包部署在tomcat步骤 1.将自己的项目打成war包. 2.将打包好的war复制到${tomcat.home}/webapps项目下. 3.在${tomcat.hom}/con ...

  6. 限制ip ssh远程登录

    有时候为了服务器的安全考虑,我们可以在服务器上做限制,禁止其他ip地址连接服务器. 方法一:修改ssh配置文件 其实做这个操作很简单,只需要改/etc/ssh/sshd_config配置文件,再最后一 ...

  7. T-SQL数据库备份

    /*1.--得到数据库的文件目录 @dbname 指定要取得目录的数据库名 如果指定的数据不存在,返回安装SQL时设置的默认数据目录 如果指定NULL,则返回默认的SQL备份目录名 */ /*--调用 ...

  8. Shell--基础知识

    变量的定义: a=1 b=hello c="hello world !" d='hello "反启" !' e=`ls`  (注意:这是反引号) 备注:=号左右 ...

  9. 沈阳网络赛J-Ka Chang【分块】【树状数组】【dfs序】

    Given a rooted tree ( the root is node 11 ) of NN nodes. Initially, each node has zero point. Then, ...

  10. Python大数据:外部数据获取(网页抓取)

    import urllib2 as url import cookielib,StringIO,gzip,json import pandas as pd import numpy as np #定义 ...