webpack Tapable官方文档(中文翻译)

本文详细介绍了Tapable包中的Hook类,包括其各种类型的构造函数,如同步、异步和Promise钩子,以及如何在Car类中使用这些Hook实现插件系统。还探讨了如何使用tap方法添加插件,拦截器和HookMap的概念,以及如何通过MultiHook重定向到多个其他Hook。
摘要由CSDN通过智能技术生成

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 暴露在一个 hooks 属性中:

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

	/* ... */
}

其他人现在可以使用这些 hooks:

const myCar = new Car();

// 使用 tap 方法添加一个消费者
myCar.hooks.brake.tap("WarningLampPlugin", () => warningLamp.on());

必须传递一个名称来识别插件/原因。

你可能会接收参数:

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

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

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);
})

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

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) => {
			// 对于 AsyncParallelHook,res 是 undefined
			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 将编译一个最有效的运行插件的方法。它根据以下情况生成代码:

  • 注册的插件数量(没有,一个,多个)
  • 注册的插件类型(同步,异步,Promise)
  • 使用的调用方法(同步,异步,Promise)
  • 参数数量
  • 是否使用拦截

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

Hook 类型
每个 hook 可以被一个或多个函数注册。它们如何执行取决于 hook 类型:

  • 基本 hook(名称中不包含“Waterfall”、“Bail”或“Loop”)。这个 hook 简单地依次调用它注册的每个函数。

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

  • Bail。一个 bail hook 允许提前退出。当任何注册的函数返回任何值时,bail hook 将停止执行其余的函数。

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

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

  • Sync。一个同步 hook 只能被同步函数注册(使用 myHook.tap())。

  • AsyncSeries。一个异步串行 hook 可以被同步、基于回调的和基于 Promise 的函数注册(使用 myHook.tap()、myHook.tapAsync() 和 myHook.tapPromise())。它们依次调用每个异步方法。

  • AsyncParallel。一个异步并行 hook 也可以被同步、基于回调的和基于 Promise 的函数注册(使用 myHook.tap()、myHook.tapAsync() 和 myHook.tapPromise())。然而,它们并行运行每个异步方法。

hook 类型体现在其类名中。例如,AsyncSeriesWaterfallHook 允许异步函数,并在串行运行它们,将每个函数的返回值传递给下一个函数。

拦截
所有 Hooks 提供额外的拦截 API:

myCar.hooks.calculateRoutes.intercept({
	call: (source, target, routesList) => {
		console.log("开始计算路线");
	},
	register: (tapInfo) => {
		// tapInfo = { type: "promise", name: "GoogleMapsPlugin", fn: ... }
		console.log(`${tapInfo.name} 正在执行它的工作`);
		return tapInfo; // 可能返回一个新的 tapInfo 对象
	}
})
  • call: (…args) => void 将 call 添加到你的拦截器中将在触发 hook 时触发。你

可以访问 hook 的参数。

  • tap: (tap: Tap) => void 将 tap 添加到你的拦截器中将在插件注册到 hook 时触发。提供的是 Tap 对象。Tap 对象不能被改变。

  • loop: (…args) => void 将 loop 添加到你的拦截器中将在循环 hook 的每个循环时触发。

  • register: (tap: Tap) => Tap | undefined 将 register 添加到你的拦截器中将在每个添加的 Tap 时触发,并允许修改它。

上下文
插件和拦截器可以选择访问一个可选的上下文对象,这个对象可以用来传递任意值给后续的插件和拦截器。

myCar.hooks.accelerate.intercept({
	context: true,
	tap: (context, tapInfo) => {
		// tapInfo = { type: "sync", name: "NoisePlugin", fn: ... }
		console.log(`${tapInfo.name} 正在执行它的工作`);

		// 如果至少有一个插件使用了 `context: true`,`context` 将作为空对象开始。
		// 如果没有插件使用 `context: true`,那么 `context` 将是 undefined。
		if (context) {
			// 可以向 `context` 添加任意属性,插件可以访问这些属性。
			context.hasMuffler = true;
		}
	}
});

myCar.hooks.accelerate.tap({
	name: "NoisePlugin",
	context: true
}, (context, newSpeed) => {
	if (context && context.hasMuffler) {
		console.log("安静...");
	} else {
		console.log("嗡嗡!");
	}
});

HookMap
HookMap 是一个帮助类,用于包含 Hooks 的 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 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
}

受保护的(仅对包含 hook 的类):

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 的类,用于将 taps 重定向到多个其他 hooks:

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

this.hooks.allHooks = new MultiHook([this.hooks.hookA, this.hooks.hookB]);
  • 37
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值