github:tapable
转载学习参考:webpack4核心模块tapable源码解析
学习参考:可能是全网最全最新最细的 webpack-tapable-2.0 的源码分析
- tapable 钩子介绍
// 第一种分类:Sync--同步钩子,只能使用tap方法注册函数;AsyncParallel--异步并行,可以使用tap、tapAsync、tapPromise注册,并行调用;AsyncSeries--可以使用tap、tapAsync、tapPromise注册,异步串行; // 第二种分类:Basic hook --基本钩子,按顺序简单的调用每一个注册的函数;Waterfall --瀑布流钩子,也是按顺序简单的调用每一个注册的函数,但它将一个返回值从每个函数传递到下一个函数;Bail -- 当任何被注册的函数返回任何内容时,钩子将停止执行其余的钩子;loop--循环钩子 const { SyncHook, SyncBailHook, SyncWaterfallHook, SyncLoopHook, AsyncParallelHook, AsyncParallelBailHook, AsyncSeriesHook, AsyncSeriesBailHook, AsyncSeriesWaterfallHook } = require("tapable");
- 本质:一种事件监听触发机制,tap、tapAsync、tapPromise进行事件注册,call、promise、callAsync进行事件的触发;不同的钩子,调用的方式不一致;
精髓:结构特别巧妙,代码复用,缓存的利用,以及new Function生成函数,牛牛的~~
// github示例
class Car {
constructor() {
// 实例,可选参数数组,定义了参数名列表
this.hooks = {
accelerate: new SyncHook(["newSpeed"]),
brake: new SyncHook(),
calculateRoutes: new AsyncParallelHook(["source", "target", "routesList"])
};
}
/* ... */
}
const myCar = new Car();
// 注册,第一个参数options对象,如果传字符串,则options = {name: options};
// 第二个参数为注册函数,参数为实例时的参数名列表
myCar.hooks.brake.tap("WarningLampPlugin", () => warningLamp.on());
myCar.hooks.accelerate.tap("LoggerPlugin", newSpeed => console.log(`Accelerating to ${newSpeed}`));
myCar.hooks.calculateRoutes.tapPromise("GoogleMapsPlugin", (source, target, routesList) => {
// return a 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);
// call the callback
callback();
});
});
myCar.hooks.calculateRoutes.tap("CachedRoutesPlugin", (source, target, routesList) => {
const cachedRoute = cache.get(source, target);
if(cachedRoute)
routesList.add(cachedRoute);
})
// 调用
class Car {
/**
* You won't get returned value from SyncHook or AsyncParallelHook,
* to do that, use SyncWaterfallHook and AsyncSeriesWaterfallHook respectively
**/
setSpeed(newSpeed) {
// following call returns undefined even when you returned values
this.hooks.accelerate.call(newSpeed);
}
useNavigationSystemPromise(source, target) {
const routesList = new List();
return this.hooks.calculateRoutes.promise(source, target, routesList).then((res) => {
// res is undefined for AsyncParallelHook
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结构一样,所从最简单的SyncHook开始
// SyncHook.js const Hook = require("./Hook"); const HookCodeFactory = require("./HookCodeFactory"); class SyncHookCodeFactory extends HookCodeFactory { // factory.create方法,在contentWithInterceptors里面,会调用content方法生成call、callAsync、promise函数 content({ onError, onDone, rethrowIfPossible }) { return this.callTapsSeries({ onError: (i, err) => onError(err), onDone, rethrowIfPossible }); } } const factory = new SyncHookCodeFactory(); const TAP_ASYNC = () => { throw new Error("tapAsync is not supported on a SyncHook"); }; const TAP_PROMISE = () => { throw new Error("tapPromise is not supported on a SyncHook"); }; // 生成调用函数的方法,也就是call、callAsync、promise方法,会在内部调用compile方法 const COMPILE = function(options) { factory.setup(this, options); return factory.create(options); }; // 定义hook(不理解这种方式和下面的extends方式有什么区别,有什么优势) function SyncHook(args = [], name = undefined) { const hook = new Hook(args, name); hook.constructor = SyncHook; hook.tapAsync = TAP_ASYNC; hook.tapPromise = TAP_PROMISE; hook.compile = COMPILE; return hook; } SyncHook.prototype = null; // 没有理解(难道是因为这样就可以防止改动到Hook原型) module.exports = SyncHook;
class SyncHook extends Hook {
tapAsync() {
throw new Error("tapAsync is not supported on a SyncHook");
}
tapPromise() {
throw new Error("tapPromise is not supported on a SyncHook");
}
compile(options) {
factory.setup(this, options);
return factory.create(options);
}
}
- 每个hook都是实例化Hook基类
const CALL_DELEGATE = function(...args) { // 赋值缓存,对于每一个实例,生成一次调用函数后,缓存~~ this.call = this._createCall("sync"); return this.call(...args); }; const CALL_ASYNC_DELEGATE = function(...args) { this.callAsync = this._createCall("async"); return this.callAsync(...args); }; const PROMISE_DELEGATE = function(...args) { this.promise = this._createCall("promise"); return this.promise(...args); }; class Hook { constructor(args = [], name = undefined) { this._args = args; this.name = name; this.taps = []; // 存储每一个注册对象,包括 name, type, fn 以及其他 options this.interceptors = []; this._call = CALL_DELEGATE; // 内部,用于重置 this.call = CALL_DELEGATE; this._callAsync = CALL_ASYNC_DELEGATE; this.callAsync = CALL_ASYNC_DELEGATE; this._promise = PROMISE_DELEGATE; this.promise = PROMISE_DELEGATE; this._x = undefined; // 用于存储tab注册的函数fn this.compile = this.compile; // ??? this.tap = this.tap; this.tapAsync = this.tapAsync; this.tapPromise = this.tapPromise; } compile(options) { throw new Error("Abstract: should be overridden"); } // 生成call方法的中间方法 _createCall(type) { return this.compile({ // 每个Hook内部实现的方法,也就是调用factory.create生成 taps: this.taps, interceptors: this.interceptors, args: this._args, type: type }); } // 真正触发的注册事件方法 _tap(type, options, fn) { if (typeof options === "string") { options = { name: options }; } else if (typeof options !== "object" || options === null) { throw new Error("Invalid tap options"); } if (typeof options.name !== "string" || options.name === "") { throw new Error("Missing name for tap"); } if (typeof options.context !== "undefined") { deprecateContext(); } options = Object.assign({ type, fn }, options); options = this._runRegisterInterceptors(options); this._insert(options); // 排序,以及存储到this.taps } tap(options, fn) { this._tap("sync", options, fn); } tapAsync(options, fn) { this._tap("async", options, fn); } tapPromise(options, fn) { this._tap("promise", options, fn); } // 运行拦截器对options进行过滤 _runRegisterInterceptors(options) { for (const interceptor of this.interceptors) { if (interceptor.register) { const newOptions = interceptor.register(options); if (newOptions !== undefined) { options = newOptions; } } } return options; } withOptions(options) { const mergeOptions = opt => Object.assign({}, options, typeof opt === "string" ? { name: opt } : opt); return { name: this.name, tap: (opt, fn) => this.tap(mergeOptions(opt), fn), tapAsync: (opt, fn) => this.tapAsync(mergeOptions(opt), fn), tapPromise: (opt, fn) => this.tapPromise(mergeOptions(opt), fn), intercept: interceptor => this.intercept(interceptor), isUsed: () => this.isUsed(), withOptions: opt => this.withOptions(mergeOptions(opt)) }; } isUsed() { return this.taps.length > 0 || this.interceptors.length > 0; } intercept(interceptor) { this._resetCompilation(); this.interceptors.push(Object.assign({}, interceptor)); if (interceptor.register) { for (let i = 0; i < this.taps.length; i++) { this.taps[i] = interceptor.register(this.taps[i]); } } } _resetCompilation() { // 重置 this.call = this._call; this.callAsync = this._callAsync; this.promise = this._promise; } _insert(item) { // 每次注册都会重置,所以有新的注册,则会重新生成调用函数 this._resetCompilation(); let before; if (typeof item.before === "string") { before = new Set([item.before]); } else if (Array.isArray(item.before)) { before = new Set(item.before); } let stage = 0; if (typeof item.stage === "number") { stage = item.stage; } let i = this.taps.length; while (i > 0) { i--; const x = this.taps[i]; // i以及i+1都先赋值为i的值 this.taps[i + 1] = x; const xStage = x.stage || 0; if (before) { // 查找插入 if (before.has(x.name)) { before.delete(x.name); continue; } if (before.size > 0) { continue; } } if (xStage > stage) { continue; } i++; break; } this.taps[i] = item; } } Object.setPrototypeOf(Hook.prototype, null); module.exports = Hook;
- 每个hook的生成函数,都是实例化HookCodeFactory基类
```js
class HookCodeFactory {
constructor(config) {
this.config = config;
this.options = undefined;
this._args = undefined;
}
/**
- 根据传入的参数生成call函数的方法
- @param options
*/
create(options) {
this.init(options);
let fn;
switch (this.options.type) {
case “sync”:
fn = new Function(
this.args(),
‘“use strict”;\n’ +
this.header() +
this.contentWithInterceptors({ // 主打生成函数方法,通过不同的参数onError,onResult,onDone处理调用
onError: err =>
throw ${err};\n
, onResult: result =>return ${result};\n
, // 处理返回值的基础代码 resultReturns: true, // 是否返回结果 onDone: () => “”, rethrowIfPossible: true // 是否抛异常 }) ); break; case “async”: fn = new Function( this.args({ after: “_callback” // async类型会多一个参数,callback }), ‘“use strict”;\n’ + this.header() + this.contentWithInterceptors({ onError: err =>_callback(${err});\n
, onResult: result =>_callback(null, ${result});\n
, onDone: () => “_callback();\n” }) ); break; case “promise”: let errorHelperUsed = false; const content = this.contentWithInterceptors({ onError: err => { errorHelperUsed = true; return_error(${err});\n
; }, onResult: result =>_resolve(${result});\n
, onDone: () => “_resolve();\n” }); let code = “”; code += ‘“use strict”;\n’; code += “return new Promise((_resolve, _reject) => {\n”; // 生成promise函数 if (errorHelperUsed) { code += “var _sync = true;\n”; code += “function _error(_err) {\n”; code += “if(_sync)\n”; code += “_resolve(Promise.resolve().then(() => { throw _err; }));\n”; code += “else\n”; code += “_reject(_err);\n”; code += “};\n”; } code += this.header(); code += content; if (errorHelperUsed) { code += “_sync = false;\n”; } code += “});\n”; fn = new Function(this.args(), code); break; } this.deinit(); // 哈哈,为了打印生成的方法 console.log((options.args || []).join(‘,’), fn.toString()); return fn; }
setup(instance, options) { instance._x = options.taps.map(t => t.fn); // 过滤注册函数fn,存储到_x对象里 }
/**
- @param options */ init(options) { this.options = options; this._args = options.args.slice(); }
deinit() { this.options = undefined; this._args = undefined; }
contentWithInterceptors(options) { if (this.options.interceptors.length > 0) { const onError = options.onError; const onResult = options.onResult; const onDone = options.onDone; // 这里回调用子类hook的content方法 return this.content( Object.assign(options, { onError: onError && (err => { let code = “”; for (let i = 0; i < this.options.interceptors.length; i++) { const interceptor = this.options.interceptors[i]; if (interceptor.error) { code +=
${this.getInterceptor(i)}.error(${err});\n
; } } code += onError(err); return code; }), onResult: onResult && (result => { let code = “”; for (let i = 0; i < this.options.interceptors.length; i++) { const interceptor = this.options.interceptors[i]; if (interceptor.result) { code +=${this.getInterceptor(i)}.result(${result});\n
; } } code += onResult(result); return code; }), onDone: onDone && (() => { let code = “”; for (let i = 0; i < this.options.interceptors.length; i++) { const interceptor = this.options.interceptors[i]; if (interceptor.done) { code +=${this.getInterceptor(i)}.done();\n
; } } code += onDone(); return code; }) }) ); } else { return this.content(options); } } // 设置变量,比如 _context,_x,_taps,_interceptors,生成拦截器的call属性 header() { let code = “”; if (this.needContext()) { code += “var _context = {};\n”; } else { code += “var _context;\n”; } code += “var _x = this._x;\n”; if (this.options.interceptors.length > 0) { code += “var _taps = this.taps;\n”; code += “var _interceptors = this.interceptors;\n”; } for (let i = 0; i < this.options.interceptors.length; i++) { const interceptor = this.options.interceptors[i]; if (interceptor.call) { code +=${this.getInterceptor(i)}.call(${this.args({ before: interceptor.context ? "_context" : undefined })});\n
; } } return code; }needContext() { for (const tap of this.options.taps) if (tap.context) return true; return false; } // 具体生成每一个注册函数的字符串 callTap(tapIndex, { onError, onResult, onDone, rethrowIfPossible }) { let code = “”; let hasTapCached = false; for (let i = 0; i < this.options.interceptors.length; i++) { const interceptor = this.options.interceptors[i]; if (interceptor.tap) { if (!hasTapCached) { code +=
var _tap${tapIndex} = ${this.getTap(tapIndex)};\n
; hasTapCached = true; } code +=${this.getInterceptor(i)}.tap(${ interceptor.context ? "_context, " : "" }_tap${tapIndex});\n
; } } code +=var _fn${tapIndex} = ${this.getTapFn(tapIndex)};\n
; const tap = this.options.taps[tapIndex]; switch (tap.type) { case “sync”: if (!rethrowIfPossible) { code +=var _hasError${tapIndex} = false;\n
; code += “try {\n”; } if (onResult) { code +=var _result${tapIndex} = _fn${tapIndex}(${this.args({ before: tap.context ? "_context" : undefined })});\n
; } else { code +=_fn${tapIndex}(${this.args({ before: tap.context ? "_context" : undefined })});\n
; } if (!rethrowIfPossible) { code += “} catch(_err) {\n”; code +=_hasError${tapIndex} = true;\n
; code += onError(“_err”); code += “}\n”; code +=if(!_hasError${tapIndex}) {\n
; } // 如果需要返回值则生成返回值代码 if (onResult) { code += onResult(_result${tapIndex}
); } // 新生成的字符串和之前生成的字符串 if (onDone) { code += onDone(); } if (!rethrowIfPossible) { code += “}\n”; } break; case “async”: let cbCode = “”; if (onResult) cbCode +=(_err${tapIndex}, _result${tapIndex}) => {\n
; else cbCode +=_err${tapIndex} => {\n
; cbCode +=if(_err${tapIndex}) {\n
; cbCode += onError(_err${tapIndex}
); cbCode += “} else {\n”; if (onResult) { cbCode += onResult(_result${tapIndex}
); } if (onDone) { cbCode += onDone(); } cbCode += “}\n”; cbCode += “}”; code +=_fn${tapIndex}(${this.args({ before: tap.context ? "_context" : undefined, after: cbCode })});\n
; break; case “promise”: code +=var _hasResult${tapIndex} = false;\n
; code +=var _promise${tapIndex} = _fn${tapIndex}(${this.args({ before: tap.context ? "_context" : undefined })});\n
; code +=if (!_promise${tapIndex} || !_promise${tapIndex}.then)\n
; code += ` throw new Error(‘Tap function (tapPromise) did not return promise (returned ‘ + _promise${tapIndex} + ‘)’);\n; code +=
_promise${tapIndex}.then(_result${tapIndex} => {\n; code +=
_hasResult${tapIndex} = true;\n; if (onResult) { code += onResult(
_result${tapIndex}); } if (onDone) { code += onDone(); } code +=
}, _err${tapIndex} => {\n; code +=
if(_hasResult${tapIndex}) throw _err${tapIndex};\n; code += onError(
_err${tapIndex}`); code += “});\n”; break; } return code; }callTapsSeries({ onError, onResult, resultReturns, onDone, doneReturns, rethrowIfPossible }) { if (this.options.taps.length === 0) return onDone(); const firstAsync = this.options.taps.findIndex(t => t.type !== “sync”); const somethingReturns = resultReturns || doneReturns || false; let code = “”; let current = onDone; for (let j = this.options.taps.length - 1; j >= 0; j–) { const i = j; const unroll = current !== onDone && this.options.taps[i].type !== “sync”; if (unroll) { code +=
function _next${i}() {\n
; code += current(); code +=}\n
; current = () =>${somethingReturns ? "return " : ""}_next${i}();\n
; } const done = current; const doneBreak = skipDone => { if (skipDone) return “”; return onDone(); }; const content = this.callTap(i, { onError: error => onError(i, error, done, doneBreak), onResult: onResult && (result => { return onResult(i, result, done, doneBreak); }), onDone: !onResult && done, rethrowIfPossible: rethrowIfPossible && (firstAsync < 0 || i < firstAsync) }); current = () => content; } code += current(); return code; }callTapsLooping({ onError, onDone, rethrowIfPossible }) { if (this.options.taps.length === 0) return onDone(); const syncOnly = this.options.taps.every(t => t.type === “sync”); let code = “”; if (!syncOnly) { code += “var _looper = () => {\n”; code += “var _loopAsync = false;\n”; } code += “var _loop;\n”; code += “do {\n”; code += “_loop = false;\n”; for (let i = 0; i < this.options.interceptors.length; i++) { const interceptor = this.options.interceptors[i]; if (interceptor.loop) { code +=
${this.getInterceptor(i)}.loop(${this.args({ before: interceptor.context ? "_context" : undefined })});\n
; } } code += this.callTapsSeries({ onError, onResult: (i, result, next, doneBreak) => { let code = “”; code +=if(${result} !== undefined) {\n
; code += “_loop = true;\n”; if (!syncOnly) code += “if(_loopAsync) _looper();\n”; code += doneBreak(true); code +=} else {\n
; code += next(); code +=}\n
; return code; }, onDone: onDone && (() => { let code = “”; code += “if(!_loop) {\n”; code += onDone(); code += “}\n”; return code; }), rethrowIfPossible: rethrowIfPossible && syncOnly }); code += “} while(_loop);\n”; if (!syncOnly) { code += “_loopAsync = true;\n”; code += “};\n”; code += “_looper();\n”; } return code; }callTapsParallel({ onError, onResult, onDone, rethrowIfPossible, onTap = (i, run) => run() }) { if (this.options.taps.length <= 1) { return this.callTapsSeries({ onError, onResult, onDone, rethrowIfPossible }); } let code = “”; code += “do {\n”; code +=
var _counter = ${this.options.taps.length};\n
; if (onDone) { code += “var _done = () => {\n”; code += onDone(); code += “};\n”; } for (let i = 0; i < this.options.taps.length; i++) { const done = () => { if (onDone) return “if(–_counter === 0) _done();\n”; else return “–_counter;”; }; const doneBreak = skipDone => { if (skipDone || !onDone) return “_counter = 0;\n”; else return “_counter = 0;\n_done();\n”; }; code += “if(_counter <= 0) break;\n”; code += onTap( i, () => this.callTap(i, { onError: error => { let code = “”; code += “if(_counter > 0) {\n”; code += onError(i, error, done, doneBreak); code += “}\n”; return code; }, onResult: onResult && (result => { let code = “”; code += “if(_counter > 0) {\n”; code += onResult(i, result, done, doneBreak); code += “}\n”; return code; }), onDone: !onResult && (() => { return done(); }), rethrowIfPossible }), done, doneBreak ); } code += “} while(false);\n”; return code; }args({ before, after } = {}) { let allArgs = this._args; if (before) allArgs = [before].concat(allArgs); if (after) allArgs = allArgs.concat(after); if (allArgs.length === 0) { return “”; } else { return allArgs.join(“, “); } }
getTapFn(idx) { return
_x[${idx}]
; }getTap(idx) { return
_taps[${idx}]
; }getInterceptor(idx) { return
_interceptors[${idx}]
; } }
module.exports = HookCodeFactory;
4. 测试生成实例
+ syncHook
```js
const SyncHook = require("../lib/SyncHook");
// 实例化 SyncHook
const sh = new SyncHook(['arg1'])
// 通过 tap 注册 handler
sh.tap('1', function(arg1, arg2) {
console.log(arg1, arg2, 1);
});
sh.tap({
name: '2',
before: '1',
}, function(arg1) {
console.log(arg1, 2);
});
sh.tap({
name: '3',
stage: -1,
}, function(arg1) {
console.log(arg1, 3);
});
// 通过 call 执行 handler
sh.call('tapable', 'tapable-2.0.0')
/**
* 生成的call函数
function anonymous(arg1) {
"use strict";
var _context;
var _x = this._x;
var _fn0 = _x[0];
_fn0(arg1);
var _fn1 = _x[1];
_fn1(arg1);
var _fn2 = _x[2];
_fn2(arg1);
}
tapable 3
tapable 2
tapable undefined 1
*/
- asyncParallelHook_1
```js const AsyncParallelHook = require(“../lib/AsyncParallelHook”);
// 创建实列 const asyncParallelHook = new AsyncParallelHook([“name”, “age”]);
// 注册事件 asyncParallelHook.tapAsync(“1”, (name, age, done) => { setTimeout(() => { console.log(“1”, name, age, new Date()); // done(); done(1); // 注意done函数,有无函数,以及有无参数 都会影响输出 }, 1000); });
asyncParallelHook.tapAsync(“2”, (name, age, done) => { // setTimeout(() => { // console.log(“2”, name, age, new Date()); // done(); // }, 2000); console.log(“2”, name, age, new Date()); done(2); });
asyncParallelHook.tapAsync(“3”, (name, age, done) => { setTimeout(() => { console.log(“3”, name, age, new Date()); done(); }, 3000); });
// 触发事件,让监听函数执行 asyncParallelHook.callAsync(“kongzhiEvent-1”, 18, () => { console.log(‘函数执行完毕’); });
/**
- function anonymous(name, age, _callback) { “use strict”; var _context; var _x = this._x; do { var _counter = 3; var _done = () => { _callback(); }; if (_counter <= 0) break; var _fn0 = _x[0]; _fn0(name, age, _err0 => { if (_err0) { if (_counter > 0) { _callback(_err0); _counter = 0; } } else { if (–_counter === 0) _done(); } }); if (_counter <= 0) break; var _fn1 = _x[1]; _fn1(name, age, _err1 => { if (_err1) { if (_counter > 0) { _callback(_err1); _counter = 0; } } else { if (–_counter === 0) _done(); } }); if (_counter <= 0) break; var _fn2 = _x[2]; _fn2(name, age, _err2 => { if (_err2) { if (_counter > 0) { _callback(_err2); _counter = 0; } } else { if (–_counter === 0) _done(); } }); } while (false);
}
*/
+ asyncParallelHook_2
```js
const AsyncParallelHook = require("../lib/AsyncParallelHook");
// 创建实列
const asyncParallelHook = new AsyncParallelHook(["name", "age"]);
// 注册事件
// 使用tapPromise注册的事件,必须返回一个Promise实列
asyncParallelHook.tapPromise("1", (name, age) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("1", name, age, new Date());
}, 1000);
});
});
asyncParallelHook.tapPromise("2", (name, age) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("2", name, age, new Date());
}, 2000);
});
});
asyncParallelHook.tapPromise("3", (name, age) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("3", name, age, new Date());
}, 3000);
});
});
// 触发事件,让监听函数执行
asyncParallelHook.promise("kongzhiEvent-1", 18);
/**
* name,age
function anonymous(name, age) {
"use strict";
return new Promise((_resolve, _reject) => {
var _sync = true;
function _error(_err) {
if (_sync)
_resolve(Promise.resolve().then(() => { throw _err; }));
else
_reject(_err);
};
var _context;
var _x = this._x;
do {
var _counter = 3;
var _done = () => {
_resolve();
};
if (_counter <= 0) break;
var _fn0 = _x[0];
var _hasResult0 = false;
var _promise0 = _fn0(name, age);
if (!_promise0 || !_promise0.then)
throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise0 + ')');
_promise0.then(_result0 => {
_hasResult0 = true;
if (--_counter === 0) _done();
}, _err0 => {
if (_hasResult0) throw _err0;
if (_counter > 0) {
_error(_err0);
_counter = 0;
}
});
if (_counter <= 0) break;
var _fn1 = _x[1];
var _hasResult1 = false;
var _promise1 = _fn1(name, age);
if (!_promise1 || !_promise1.then)
throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise1 + ')');
_promise1.then(_result1 => {
_hasResult1 = true;
if (--_counter === 0) _done();
}, _err1 => {
if (_hasResult1) throw _err1;
if (_counter > 0) {
_error(_err1);
_counter = 0;
}
});
if (_counter <= 0) break;
var _fn2 = _x[2];
var _hasResult2 = false;
var _promise2 = _fn2(name, age);
if (!_promise2 || !_promise2.then)
throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise2 + ')');
_promise2.then(_result2 => {
_hasResult2 = true;
if (--_counter === 0) _done();
}, _err2 => {
if (_hasResult2) throw _err2;
if (_counter > 0) {
_error(_err2);
_counter = 0;
}
});
} while (false);
_sync = false;
});
}
*/
- asyncParallelHook_3
```js const AsyncParallelHook = require(“../lib/AsyncParallelHook”);
// 创建实列 const asyncParallelHook = new AsyncParallelHook([“name”, “age”]);
// 注册事件 asyncParallelHook.tapAsync(“1”, (name, age, done) => { setTimeout(() => { console.log(“1”, name, age, new Date()); // done(); done(1); // 注意done函数,有无函数,以及有无参数 都会影响输出 }, 1000); });
asyncParallelHook.tapAsync(“2”, (name, age, done) => { // setTimeout(() => { // console.log(“2”, name, age, new Date()); // done(); // }, 2000); console.log(“2”, name, age, new Date()); done(2); });
asyncParallelHook.tapAsync(“3”, (name, age, done) => { setTimeout(() => { console.log(“3”, name, age, new Date()); done(); }, 3000); });
// 触发事件,让监听函数执行 asyncParallelHook.promise(“kongzhiEvent-1”, 18).then(() => { console.log(‘函数执行完毕success’); }, err => { console.log(‘函数执行完毕fail’, err); });
/**
- function anonymous(name, age) { “use strict”; return new Promise((_resolve, _reject) => { var _sync = true; // 顺序执行标识~ function _error(_err) { if (_sync) _resolve(Promise.resolve().then(() => { throw _err; })); else _reject(_err); }; var _context; var _x = this._x; do { var _counter = 3; var _done = () => { _resolve(); }; if (_counter <= 0) break; var _fn0 = _x[0]; _fn0(name, age, _err0 => { if (_err0) { if (_counter > 0) { _error(_err0); _counter = 0; } } else { if (–_counter === 0) _done(); } }); if (_counter <= 0) break; var _fn1 = _x[1]; _fn1(name, age, _err1 => { if (_err1) { if (_counter > 0) { _error(_err1); _counter = 0; } } else { if (–_counter === 0) _done(); } }); if (_counter <= 0) break; var _fn2 = _x[2]; _fn2(name, age, _err2 => { if (_err2) { if (_counter > 0) { _error(_err2); _counter = 0; } } else { if (–_counter === 0) _done(); } }); } while (false); _sync = false; }); }
// — console—– 2 kongzhiEvent-1 18 Sat Feb 01 2020 00:31:45 GMT+0800 (CST) {}
函数执行完毕fail 2
1 kongzhiEvent-1 18 Sat Feb 01 2020 00:31:46 GMT+0800 (CST) {} */
+ 利用__tests__里debug测试文件
```json
// launch.json
{
"type": "node",
"request": "launch",
"name": "tapable的jest调试",
"cwd": "${workspaceFolder}/tapable",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/tapable/lib/__tests__/AsyncParallelHooks.js",
"outFiles": [
"${workspaceFolder}/**/*.js"
],
"runtimeExecutable": "npm",// node 的可执行程序
"runtimeArgs": [ // 类似于命令行参数
"run-script", "debug"
],
"port": 9229,
// 使用什么终端工具。integratedTerminal 表示使用 Visual Studio Code 内置的终端工具,也可以配置成使用操作系统默认的终端工具。
"console": "integratedTerminal"
}
// package.json
"scripts": {
"debug": "node --inspect-brk ./node_modules/jest/bin/jest --runInBand --no-cache --no-watchman"
},
- 低版本兼容
版本2xx以上已经没有Tapable.js文件,但由于webpack4用的是1xx,其中Compiler extends Tapable,而且插件注册也是提供带有 apply 方法的对象,里面利用compiler.plugin来注册,所以需要兼容
/* 创建插件 */
// 一个 JavaScript 命名函数。
function MyExampleWebpackPlugin() {
};
// 在插件函数的 prototype 上定义一个 `apply` 方法。
MyExampleWebpackPlugin.prototype.apply = function(compiler) {
// 指定一个挂载到 webpack 自身的事件钩子。(其实这里,实际上执行的是Tapable的tap注册)
compiler.plugin('webpacksEventHook', function(compilation /* 处理 webpack 内部实例的特定数据。*/, callback) {
console.log("This is an example plugin!!!");
// 功能完成后调用 webpack 提供的回调。
callback();
});
};
// Tapable.js
const util = require("util");
const SyncBailHook = require("./SyncBailHook");
function Tapable() {
// 声明同步保险钩子
this._pluginCompat = new SyncBailHook(["options"]);
// 注册 handler,主要是为了将 pluginName camelize 化。
this._pluginCompat.tap(
{
name: "Tapable camelCase",
stage: 100
},
options => {
options.names.add(
options.name.replace(/[- ]([a-z])/g, (str, ch) => ch.toUpperCase())
);
}
);
// 在 hooks 属性上对应的钩子上注册 handler
this._pluginCompat.tap(
{
name: "Tapable this.hooks",
stage: 200
},
options => {
let hook;
for (const name of options.names) {
hook = this.hooks[name];
if (hook !== undefined) {
break;
}
}
if (hook !== undefined) {
const tapOpt = {
name: options.fn.name || "unnamed compat plugin",
stage: options.stage || 0
};
if (options.async) hook.tapAsync(tapOpt, options.fn);
else hook.tap(tapOpt, options.fn);
return true;
}
}
);
}
module.exports = Tapable;
Tapable.addCompatLayer = function addCompatLayer(instance) {
Tapable.call(instance);
instance.plugin = Tapable.prototype.plugin;
instance.apply = Tapable.prototype.apply;
};
// 注册 handler,实际上会走到 _pluginCompat 属性上的第二个 handler,进而在对应的 hooks 注册了 handler。
Tapable.prototype.plugin = util.deprecate(function plugin(name, fn) {
if (Array.isArray(name)) {
name.forEach(function(name) {
this.plugin(name, fn);
}, this);
return;
}
const result = this._pluginCompat.call({
name: name,
fn: fn,
names: new Set([name])
});
if (!result) {
throw new Error(
`Plugin could not be registered at '${name}'. Hook was not found.\n` +
"BREAKING CHANGE: There need to exist a hook at 'this.hooks'. " +
"To create a compatibility layer for this hook, hook into 'this._pluginCompat'."
);
}
}, "Tapable.plugin is deprecated. Use new API on `.hooks` instead");
Tapable.prototype.apply = util.deprecate(function apply() {
for (var i = 0; i < arguments.length; i++) {
arguments[i].apply(this);
}
}, "Tapable.apply is deprecated. Call apply on the plugin directly instead");