tapable 中文文档

Tapable

tapable 公开了许多Hook类,可以用来为插件创建钩子实例。

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

安装

npm install --save tapable

使用

所有Hook构造函数都接受一个可选参数,它是一个字符串形式的参数列表。

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

最佳实践是在一个hooks属性中公开类的所有hook:

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
myCar.hooks.brake.tap("WarningLampPlugin", () => warningLamp.on());

需要传递一个名称来识别插件。
你可能会收到如下参数:

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

对于同步钩子,tap是添加插件的唯一有效方法。异步钩子也支持异步插件:

myCar.hooks.calculateRoutes.tapPromise("GoogleMapsPlugin", (source, target, routesList) => {
	// 返回一个 promise 对象
	return google.maps.findRoute(source, target).then(route => {
		routesList.add(route);
	});
});
myCar.hooks.calculateRoutes.tapAsync("BingMapsPlugin", (source, target, routesList, callback) => {
	bing.findRoute(source, target, (err, route) => {
		if(err) return callback(err);
		routesList.add(route);
		// 调用回调
		callback();
	});
});

// 你仍然可以使用同步插件
myCar.hooks.calculateRoutes.tap("CachedRoutesPlugin", (source, target, routesList) => {
	const cachedRoute = cache.get(source, target);
	if(cachedRoute)
		routesList.add(cachedRoute);
})

声明这些钩子的类需要调用它们:

class Car {
	/**
	  * 你不会从SyncHook或AsyncParallelHook获得返回值,
	  * 要做到这一点,分别使用SyncWaterfallHook和AsyncSeriesWaterfallHook
	 **/

	setSpeed(newSpeed) {
		// 后面的调用即使你返回值,也会返回undefined
		this.hooks.accelerate.call(newSpeed);
	}

	useNavigationSystemPromise(source, target) {
		const routesList = new List();
		return this.hooks.calculateRoutes.promise(source, target, routesList).then((res) => {
			// res在AsyncParallelHook中是undefind
			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());
		});
	}
}

Hook会以最有效的方式编译一个方法来运行插件。它生成的代码取决于:

注册插件的数量(无,一个,多个)
注册插件的类型(sync, async, promise)
所使用的调用方法(sync, async, promise)
参数的个数
是否使用拦截

这确保了尽可能快的执行。

Hook 类型

每个hook都可以使用一个或多个函数。它们如何执行取决于hook类型:

同步基本的钩子 (名称中没有“Waterfall”,“Bail”或“Loop”)。这个钩子只是依次调用它注册的每个函数,上一个函数执行完成,进行下一个函数。

Waterfal: waterfall hook 也会依次调用它注册的每个函数。与基本钩子不同,它将每个函数的返回值传递给下一个函数。

Bail: Bail hook 允许提前退出。当任何被选中的函数返回任何内容时,保释钩子将停止执行剩下的内容。

Loop: 当一个loop hook中的插件返回一个非undefined值时,钩子将从第一个插件重新启动。它会循环,直到所有插件返回undefined。

此外,hook可以是同步的,也可以是异步的。为了反映这一点,有“Sync”,“AsyncSeries”和“AsyncParallel” hook类:

Sync: Sync hook只能在同步函数中使用(使用myHook.tap())。

AsyncSeries: AsyncSeries hook可以通过同步、基于回调和基于承诺的函数(使用myHook.tap()、myHook.tapAsync()和myHook.tapPromise())来实现。它们依次船型调用每个异步方法

AsyncParallel: AsyncParallel hook也可以通过同步、基于回调和基于承诺的函数(使用myHook.tap()、myHook.tapAsync()和myHook.tapPromise())来实现。然而,它们并行地运行每个异步方法。

钩子类型反映在它的类名中。例如,AsyncSeriesWaterfallHook允许异步函数并串联运行它们,将每个函数的返回值传递给下一个函数。

拦截

所有钩子都提供了一个额外的拦截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; // 可能返回一个新的tapInfo对象
	}
})

call: (…args) => 当钩子被触发时,将会触发对拦截器的调用。您可以访问hook参数。

tap: (tap: Tap) => 在你的拦截器中添加tap会在插件插入钩子时触发。提供的是tap对象。tap对象不能被改变。

loop: (…args) => 在你的拦截器中添加loop会触发一个循环钩子的每个循环。

register: (tap: Tap) => 为你的拦截器添加寄存器将会触发每次添加的tap,并允许修改它。

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: true`, `context`开始时为空对象。
		// 如果没有插件使用' context: true ',那么' context '是未定义的。
		if (context) {
			// 任意属性都可以添加到“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是带有钩子的Map的助手类

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

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

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

Hook/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,
	intercept: (interceptor: HookMapInterceptor) => void
}

interface HookMapInterceptor {
	factory: (key: any, hook: Hook) => Hook
}

interface Tap {
	name: string,
	type: string
	fn: Function,
	stage: number,
	context: boolean,
	before?: string | Array
}

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

一个类似钩子的helper类,重定向点击到多个其他钩子:

const { MultiHook } = require("tapable");

this.hooks.allHooks = new MultiHook([this.hooks.hookA, this.hooks.hookB]);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Compiled with problems:X ERROR in ./node_modules/pdfjs-dist/es5/build/pdf.worker.js (./node_modules/vue-pdf/node_modules/worker-loader/dist/cjs.js!./node_modules/pdfjs-dist/es5/build/pdf.worker.js) Module build failed (from ./node_modules/vue-pdf/node_modules/worker-loader/dist/cjs.js): Error: MainTemplate.hooks.hotBootstrap has been removed (use your own RuntimeModule instead) at Object.tap (D:\newtest\node_modules\webpack\lib\MainTemplate.js:215:12) at WebWorkerMainTemplatePlugin.apply (D:\newtest\node_modules\vue-pdf\node_modules\webpack\lib\webworker\WebWorkerMainTemplatePlugin.js:139:35) at D:\newtest\node_modules\vue-pdf\node_modules\webpack\lib\webworker\WebWorkerTemplatePlugin.js:16:39 at Hook.eval [as call] (eval at create (D:\newtest\node_modules\tapable\lib\HookCodeFactory.js:19:10), <anonymous>:7:1) at Hook.CALL_DELEGATE [as _call] (D:\newtest\node_modules\tapable\lib\Hook.js:14:14) at Compiler.newCompilation (D:\newtest\node_modules\webpack\lib\Compiler.js:1121:30) at D:\newtest\node_modules\webpack\lib\Compiler.js:1166:29 at Hook.eval [as callAsync] (eval at create (D:\newtest\node_modules\tapable\lib\HookCodeFactory.js:33:10), <anonymous>:24:1) at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (D:\newtest\node_modules\tapable\lib\Hook.js:18:14) at Compiler.compile (D:\newtest\node_modules\webpack\lib\Compiler.js:1161:28) at Compiler.runAsChild (D:\newtest\node_modules\webpack\lib\Compiler.js:561:8) at Object.pitch (D:\newtest\node_modules\vue-pdf\node_modules\worker-loader\dist\index.js:115:19)
最新发布
06-06

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值