tapable 原理解析 2
本文查看tapable中 sync 方式的方法運(yùn)行的方式以及context和intercept的運(yùn)行邏輯
1. SyncHook
執(zhí)行下面的代碼
const { SyncHook } = require('../lib/index')
const hook = new SyncHook(["arg1", "arg2"]);
hook.tap('1', function (arg1, arg2) {
console.log('1', arg1, arg2)
})
hook.tap('2', function (arg1, arg2) {
console.log('2', arg1, arg2)
})
hook.tap('3', function (arg1, arg2) {
console.log('3', arg1, arg2)
})
hook.call('a1', 'a2', 'a3')
得到的函數(shù)和輸出結(jié)果:
function anonymous(arg1, arg2) {
"use strict";
var _context;
var _x = this._x;
var _fn0 = _x[0];
_fn0(arg1, arg2);
var _fn1 = _x[1];
_fn1(arg1, arg2);
var _fn2 = _x[2];
_fn2(arg1, arg2);
}
/* 輸出
1 a1 a2
2 a1 a2
3 a1 a2
*/
可以看到SyncHook的調(diào)用很簡(jiǎn)單,順序同步執(zhí)行不關(guān)心結(jié)果。
2. SyncBailHook
執(zhí)行代碼
const hook = new SyncBailHook(["arg1", "arg2"]);
// ...
// 與前面一樣
得到的函數(shù)和輸出結(jié)果:
function anonymous(arg1, arg2) {
"use strict";
var _context;
var _x = this._x;
var _fn0 = _x[0];
var _result0 = _fn0(arg1, arg2);
if (_result0 !== undefined) {
return _result0;
;
} else {
var _fn1 = _x[1];
var _result1 = _fn1(arg1, arg2);
if (_result1 !== undefined) {
return _result1;
;
} else {
var _fn2 = _x[2];
var _result2 = _fn2(arg1, arg2);
if (_result2 !== undefined) {
return _result2;
;
} else {
}
}
}
}
可以看到SyncBailHook的調(diào)用也很簡(jiǎn)單,只要執(zhí)行過程中一個(gè)訂閱函數(shù)返回值不為undefined就會(huì)暫停后續(xù)訂閱的執(zhí)行
3. SyncWaterfallHook
const hook = new SyncWaterfallHook(["arg1", "arg2"]);
// ...
// 與前面一樣
得到的函數(shù)和輸出結(jié)果:
function anonymous(arg1, arg2) {
"use strict";
var _context;
var _x = this._x;
var _fn0 = _x[0];
var _result0 = _fn0(arg1, arg2);
if (_result0 !== undefined) {
arg1 = _result0;
}
var _fn1 = _x[1];
var _result1 = _fn1(arg1, arg2);
if (_result1 !== undefined) {
arg1 = _result1;
}
var _fn2 = _x[2];
var _result2 = _fn2(arg1, arg2);
if (_result2 !== undefined) {
arg1 = _result2;
}
return arg1;
}
可以看到SyncWaterfallHook的調(diào)用也很簡(jiǎn)單,執(zhí)行過程中會(huì)使用上一個(gè)訂閱的返回值。
4. SyncLoopHook
const hook = new SyncLoopHook(["arg1", "arg2"]);
// ...
// 與前面一樣
得到的函數(shù)和輸出結(jié)果:
function anonymous(arg1, arg2) {
"use strict";
var _context;
var _x = this._x;
var _loop;
do {
_loop = false;
var _fn0 = _x[0];
var _result0 = _fn0(arg1, arg2);
if (_result0 !== undefined) {
_loop = true;
} else {
var _fn1 = _x[1];
var _result1 = _fn1(arg1, arg2);
if (_result1 !== undefined) {
_loop = true;
} else {
var _fn2 = _x[2];
var _result2 = _fn2(arg1, arg2);
if (_result2 !== undefined) {
_loop = true;
} else {
if (!_loop) {
}
}
}
}
} while (_loop);
}
可以看到SyncLoopHook的調(diào)用也很簡(jiǎn)單,只要執(zhí)行過程中一個(gè)訂閱函數(shù)返回值不為undefined,函數(shù)就會(huì)重新從第一個(gè)訂閱函數(shù)再次按順序執(zhí)行
5. SyncHook 結(jié)合context
執(zhí)行代碼
const hook = new SyncLoopHook(["arg1", "arg2"]);
hook.tap('1', function (arg1, arg2) {
console.log('1', arg1, arg2)
})
hook.tap('2', function (arg1, arg2) {
console.log('2', arg1, arg2)
})
hook.tap('3', function (arg1, arg2) {
console.log('3', arg1, arg2)
})
hook.intercept({
call: (source, target, routesList) => {
console.log("intercept call");
},
loop: (source, target, routesList) => {
console.log("intercept loop");
},
tap: (source, target, routesList) => {
console.log("intercept tap");
},
register: (tapInfo) => {
console.log("intercept register");
console.log(`${tapInfo.name} is doing its job`);
return tapInfo; // may return a new tapInfo object
}
})
hook.call('a1', 'a2', 'a3')
得到的函數(shù)和輸出結(jié)果:
function anonymous(arg1, arg2) {
"use strict";
var _context = {};
var _x = this._x;
var _fn0 = _x[0];
_fn0(_context, arg1, arg2);
var _fn1 = _x[1];
_fn1(arg1, arg2);
var _fn2 = _x[2];
_fn2(_context, arg1, arg2);
}
/* 輸出
1 a1 a2
2 a1 a2
okk
3 a1 a2
*/
當(dāng)在訂閱時(shí)開啟context選項(xiàng),在調(diào)用過程中就會(huì)給調(diào)用方法第一個(gè)參數(shù)傳遞context對(duì)象??梢栽趫?zhí)行過程中在不同的訂閱函數(shù)中傳遞數(shù)據(jù)
6. SyncHook 結(jié)合intercept
intercept代碼執(zhí)行過程
// intercept方法
{
this.interceptors.push (interceptor));
if (interceptor.register) {
for (let i = 0; i < this.taps.length; i++) {
this.taps[i] = interceptor.register(this.taps[i]);
}
}
}
// 同時(shí)在tap時(shí)調(diào)用 interceptor.register,
{
for (const interceptor of this.interceptors) {
if (interceptor.register) {
const newOptions = interceptor.register(options);
if (newOptions !== undefined) {
options = newOptions;
}
}
}
}
可以看出register方法使用來接收原來的對(duì)訂閱的函數(shù)進(jìn)行攔截處理。
執(zhí)行代碼
const { SyncHook } = require('../lib/index')
const hook = new SyncHook(["arg1", "arg2"]);
hook.tap('1', function (arg1, arg2) {
console.log('1', arg1, arg2)
})
hook.tap('2', function (arg1, arg2) {
console.log('2', arg1, arg2)
})
hook.tap('3', function (arg1, arg2) {
console.log('3', arg1, arg2)
})
hook.intercept({
call: (source, target, routesList) => {
console.log("intercept call");
},
tap: (source, target, routesList) => {
console.log("intercept tap");
},
register: (tapInfo) => {
console.log("intercept register");
console.log(`${tapInfo.name} is doing its job`);
return tapInfo; // may return a new tapInfo object
}
})
hook.call('a1', 'a2', 'a3')
得到的函數(shù)和輸出結(jié)果:
function anonymous(arg1, arg2) {
"use strict";
var _context;
var _x = this._x;
var _taps = this.taps;
var _interceptors = this.interceptors;
_interceptors[0].call(arg1, arg2);
var _loop;
do {
_loop = false;
_interceptors[0].loop(arg1, arg2);
var _tap0 = _taps[0];
_interceptors[0].tap(_tap0);
var _fn0 = _x[0];
var _result0 = _fn0(arg1, arg2);
if (_result0 !== undefined) {
_loop = true;
} else {
var _tap1 = _taps[1];
_interceptors[0].tap(_tap1);
var _fn1 = _x[1];
var _result1 = _fn1(arg1, arg2);
if (_result1 !== undefined) {
_loop = true;
} else {
var _tap2 = _taps[2];
_interceptors[0].tap(_tap2);
var _fn2 = _x[2];
var _result2 = _fn2(arg1, arg2);
if (_result2 !== undefined) {
_loop = true;
} else {
if (!_loop) {
}
}
}
}
} while (_loop);
}
/* 輸出:
intercept register
1 is doing its job
intercept register
2 is doing its job
intercept register
3 is doing its job
current fn content:
intercept call
intercept loop
intercept tap
1 a1 a2
intercept tap
2 a1 a2
intercept tap
3 a1 a2
*/
調(diào)用hook interceptor 方法,根據(jù)傳入配置的不同會(huì)再特定的時(shí)候?qū)?shù)或者執(zhí)行時(shí)進(jìn)行攔截。
上述打印的函數(shù)中展示了在發(fā)布時(shí),對(duì)于interceptor中的call,loop和tap鉤子的調(diào)用過程;
loop方法只在有循環(huán)的條件時(shí)調(diào)用。
剩下的register方法是在訂閱時(shí)攔截訂閱函數(shù)的參數(shù),處理后返回參數(shù)。
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]);
}
}
}