最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • webrpack之 tapable

    正文概述 掘金(langyanyanglei)   2020-11-29   497

    webpack之tapable

    近期研究webpack loader和plugin编写,发现涉及到tapable,闲来无事翻翻源码结合使用方法记录一下

    原文 github.com/webpack/tap…

    一、钩子类型

    tapable提供了很多钩子(Hook classes)

    const {
    	SyncHook,                    // 同步钩子 从上到下顺序执行
    	SyncBailHook,                // 同步早退钩子 从上到下顺序执行,遇到返回值不是undefined的注册函数时停止执行
    	SyncWaterfallHook,           // 同步瀑布钩子 从上到下执行,依次将返回值传递给下一个函数
    	SyncLoopHook,                // 同步循环钩子 从上到下执行,某个函数可能会执行好几遍,当返回值是undefined会继续执行下个函数
    	AsyncParallelHook,           // 异步并发钩子 异步并行
    	AsyncParallelBailHook,       // 异步并发可早退钩子 异步并行熔断
    	AsyncSeriesHook,             // 异步顺序钩子   异步串行
    	AsyncSeriesBailHook,         // 异步顺序可早退钩子 异步串行熔断
    	AsyncSeriesWaterfallHook     // 异步顺序瀑布钩子   异步串行值传递【瀑布】
     } = require("tapable");
    

    二、搭建项目

    github.com/17139313271…

    三、钩子使用方法

    3.1 同步钩子-SyncHook

    import { SyncHook } from '../table/lib';
    
    const hook = new SyncHook(); // 创建钩子对象
    hook.tap('logPlugin', () => console.log('注册了')); // tap方法注册钩子回调
    hook.call(); // call方法调用钩子,打印出‘被勾了’三个字
    

    3.2 同步早退钩子-SyncBailHook

    SyncBailHook就是根据每一步返回的值来决定要不要继续往下走,如果return了一个非undefined的值 那就不会往下走,注意 如果什么都不return 也相当于return了一个undefined。

    import { SyncBailHook } from '../table/lib';
    
    const hook = new SyncBailHook();
    hook.tap('SyncBailHook1', () => console.log(`钩子1`));
    hook.tap('SyncBailHook2', () => {console.log(`钩子2`) ; return 1});
    hook.tap('SyncBailHook3', () => console.log(`钩子3`));
    
    hook.call(); // 会打印‘钩子1’‘钩子2’‘钩子3’
    

    3.3 同步瀑布钩子-SyncWaterfallHook

    它的每一步都依赖上一步的执行结果,也就是上一步return的值就是下一步的参数。

    import { SyncWaterfallHook  } from '../table/lib';
    
    const hook = new SyncWaterfallHook(["newSpeed"]);
    hook.tap('SyncWaterfallHook1', (speed) => { console.log(`增加到${speed}`); return speed + 100; });
    hook.tap('SyncWaterfallHook2', (speed) => { console.log(`增加到${speed}`); return speed + 50; });
    hook.tap('SyncWaterfallHook3', (speed) => { console.log(`增加到${speed}`); });
    
    hook.call(50); // 打印‘增加到50’‘增加到150’‘增加到200’
    

    3.4 同步循环钩子 -SyncLoopHook

    SyncLoopHook是同步的循环钩子,它的插件如果返回一个非undefined。就会一直执行这个插件的回调函数,直到它返回undefined。

    import { SyncLoopHook } from '../table/lib';
    
    let index = 0;
    const hook = new SyncLoopHook();
    hook.tap('startPlugin1', () => {
        console.log(`执行`);
        if (index < 5) {
            index++;
            return 1;
        }
    }); 
    
    hook.tap('startPlugin2', () => {
        console.log(`执行2`);
    });
    
    hook.call(); // 打印‘执行’6次,打印‘执行2’一次。
    

    3.5异步并发钩子-AsyncParallelHook

    当所有的异步任务执行结束后,再最终的回调中执行接下来的代码

    import { AsyncParallelHook } from '../table/lib';
    
    const hook = new AsyncParallelHook();
    hook.tapAsync('calculateRoutesPlugin1', (callback) => {
        setTimeout(() => {
            console.log('异步事件1');
            callback();
        }, 1000);
    });
    
    hook.tapAsync('calculateRoutesPlugin2', (callback) => {
        setTimeout(() => {
            console.log('异步事件2');
            callback();
        }, 2000);
    });
    
    hook.callAsync(() => { console.log('最终的回调'); }); // 会在1s的时候打印‘异步事件1’。2s的时候打印‘异步事件2’。紧接着打印‘最终的回调’
    

    3.6异步并发可早退钩子-AsyncParallelBailHook

    import { AsyncParallelBailHook } from '../table/lib';
    
    const hook = new AsyncParallelBailHook();
    hook.tapAsync('calculateRoutesPlugin1', (callback) => {
        setTimeout(() => {
            console.log('异步事件1');
            callback(1);
        }, 1000);
    });
    
    hook.tapAsync('calculateRoutesPlugin2', (callback) => {
        setTimeout(() => {
            console.log('异步事件2');
            callback();
        }, 2000);
    });
    
    hook.callAsync((result) => { console.log('最终的回调',result); }); // 会在1s的时候打印‘异步事件1’,紧接着打印‘最终的回调’,2s的时候打印‘异步事件2’。
    

    3.7 异步顺序钩子- AsyncSeriesHook

    import { AsyncSeriesHook } from '../table/lib';
    
    const hook = new AsyncSeriesHook();
    hook.tapPromise('calculateRoutesPlugin1', () => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log('异步事件1');
                resolve();
            }, 1000);
        });
    });
    
    hook.tapPromise('calculateRoutesPlugin2', () => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log('异步事件2');
                resolve();
            }, 2000);
        });
    });
    
    hook.then(() => { console.log('最终的回调'); });
    // 1s过后,打印异步事件1,再过2s(而不是到了第2s,而是到了第3s),打印异步事件2,再立马打印最终的回调。
    

    3.8 异步顺序可早退钩子-AsyncSeriesBailHook

    import { AsyncSeriesBailHook } from '../table/lib';
    
    const hook = new AsyncSeriesBailHook();
    hook.tapPromise('calculateRoutesPlugin1', () => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log('异步事件1');
                resolve();
            }, 1000);
        });
    });
    
    hook.tapPromise('calculateRoutesPlugin2', () => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log('异步事件2');
                resolve();
            }, 2000);
        });
    });
    
    hook.then(() => { console.log('最终的回调'); });
    // 1s过后,打印异步事件1,立马打印最终的回调,不会再执行异步事件2了。
    

    3.9 异步顺序瀑布钩子-AsyncSeriesWaterfallHook

    import { AsyncSeriesWaterfallHook } from '../table/lib';
    
    const hook = new AsyncSeriesWaterfallHook();
    hook.tapPromise('calculateRoutesPlugin1', () => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log('异步事件1', result);
    
                resolve(1);
            }, 1000);
        });
    });
    
    hook.tapPromise('calculateRoutesPlugin2', () => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log('异步事件2', result);
                resolve(2);
            }, 2000);
        });
    });
    
    hook.then(() => { console.log('最终的回调'); });
    //1s过后,打印异步事件1 undefined,再过2s打印异步事件2 北京,然后立马打印最终的回调。
    

    四、从源码分析钩子实现原理

    4.1 流程分析

    1. tapable 源码使用工厂模式和模板模式,Hook.js 抽离了功能函数,便于其他钩子函数继承,HookCodeFactory.js则使用模板模式,根据钩子函数的不同生成不同的执行函数返回调用的_call()

    2. 注册过程

      tapable注册方式有三种: tap、tapAsync和tapPromise,但过程基本都一样,都会调用 _insert()

      HOOK.js
         
         
      _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];
      		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;
      	console.log(this.taps)
      }
       1. item 对象是根据注册方式的不同生成不同的对象:
           tap:              { type: "sync", fn: fn ,name: name}
           tapAsync:         { type: "async", fn: fn ,name: name}
           tapPromise:       { type: "promise", fn: fn ,name: name}
           后面的函数执行都会按照注册的type类型来执行
       2. _insert() 作用: 根据注册类型的不同生成不同的对象并存储在 taps[] 中    
      
    3. 调用过程

      3.1tapable调用方式有三种: call、promise和callAsync,调用createCompileDelegate(), 传入相应的类型

      Hook.js
         
      function createCompileDelegate(name, type) {
      	return function lazyCompileHook(...args) {
      		this[name] = this._createCall(type);
      		console.log(this[name])
      		return this[name](...args);
      	};
      }
         
      .....
      	_createCall(type) {
      		return this.compile({
      			taps: this.taps,
      			interceptors: this.interceptors,
      			args: this._args,
      			type: type
      		});
      	}
      ....
         
      _createCall() 函数将参数传给各个钩子的compile()函数,用来生成回调函数  
      

      3.2各个钩子函数都会继承两个函数:

    ​ a. conent() 根据钩子函数的不同生成不同的执行函数即 _call()

    ​ b. compile(), 此函数会调用 HookCodeFactory.js 的两个函数 setup() 和 create()

    setup(instance, options) {
    	instance._x = options.taps.map(t => t.fn);
    }
    // 	将 optiuon数组保存的注册的函数过滤出来赋值给当前钩子函数的 ._X
    create(options) {
      this.init(options);
      switch (this.options.type) {
    	 case "sync":
    		fn = new Function(
    			this.args(),
    			'"use strict";\n' +
    			 this.header() +
    			 this.content({
    			   onError: err => `throw ${err};\n`,
    			   onResult: result => `return ${result};\n`,
    			   resultReturns: true,
    			   onDone: () => "",
    				 rethrowIfPossible: true
    			})
    		);
    		break;
            .....
    	}
    	this.deinit();
    	return fn;
    }
    
    init(options) {
    	this.options = options;
    	this._args = options.args.slice();	
    }
    
    1. init()函数 
      这个函数很简单,但注意 _args 这个数组的由来, 他是lazyCompileHook()传入的参数,即 call、promise和callAsync 这三个回调函数传入的参数
    2.create 根据注册钩子的类型调用各个钩子函数的 content()生成执行函数 fn
    也就是说我们调用(call())的时候其实是执行下面这样一个函数,
    anonymous() {
     "use strict";
      var _context;
      var _x = this._x;
      var _fn0 = _x[0];
      _fn0();
      var _fn1 = _x[1];  // 多个注册函数会生成多个
      _fn1();
    }
    

    4.2 SyncHook的实现

    //content函数
    content({ onError, onDone, rethrowIfPossible }) {
    		return this.callTapsSeries({
    			onError: (i, err) => onError(err),
    			onDone,
    			rethrowIfPossible
    		});
    	}
    
    // 生成的执行函数
    anonymous() {
     "use strict";
      var _context;
      var _x = this._x;
      var _fn0 = _x[0];
       _fn0();
       var _fn1 = _x[1];
       _fn1();
    }
    

    SyncHook 钩子生成多个 fn()函数,call()调用的时候挨个执行,比较简单

    4.3 SyncBailHook

    前面介绍使用方法时说过此钩子根据返回值判断是否继续执行,它的内部构造与 SyncHook稍有不同:

    // content 函数
    content({ onError, onResult, resultReturns, onDone, rethrowIfPossible }) {
    		return this.callTapsSeries({
    			onError: (i, err) => onError(err),
    			onResult: (i, result, next) =>
    				`if(${result} !== undefined) {\n${onResult(
    					result
    				)};\n} else {\n${next()}}\n`,
    			resultReturns,
    			onDone,
    			rethrowIfPossible
    		});
    }
    
    //生成的执行函数
    (function anonymous() {
      "use strict";
       var _context;
       var _x = this._x;
       var _fn0 = _x[0];
       var _result0 = _fn0();
       if(_result0 !== undefined) {   // 递归生成判断条件
         return _result0;
       } else {
        var _fn1 = _x[1];
        var _result1 = _fn1();
        if(_result1 !== undefined) {
           return _result1;
        } else {
          var _fn2 = _x[2];
          var _result2 = _fn2();
          if(_result2 !== undefined) {
            return _result2;
          } else {}
        }
      }
    })
    

    SyncBailHook 的 content函数 的 onResult 多一个判断条件,判断上一个钩子函数的执行结果来决定是否继续执行

    4.4 SyncWaterfallHook

    //content函数
    content({ onError, onResult, resultReturns, rethrowIfPossible }) {
    		return this.callTapsSeries({
    			onError: (i, err) => onError(err),
    			onResult: (i, result, next) => {
    				let code = "";
    				code += `if(${result} !== undefined) {\n`;
    				code += `${this._args[0]} = ${result};\n`;  //与SyncBailHook 相比多加赋值的操作
    				code += `}\n`;
    				code += next(); 
    				return code;
    			},
    			onDone: () => onResult(this._args[0]),
    			doneReturns: resultReturns,
    			rethrowIfPossible
    		});
    }
    //生成的函数
    (function anonymous(newSpeed) {
      "use strict";
      var _context;
      var _x = this._x;
      var _fn0 = _x[0];
      var _result0 = _fn0(newSpeed);
      if(_result0 !== undefined) {
        newSpeed = _result0;
       }
       var _fn1 = _x[1];
       var _result1 = _fn1(newSpeed);
      if(_result1 !== undefined) {
        newSpeed = _result1;
       }
       var _fn2 = _x[2];
      var _result2 = _fn2(newSpeed);
      if(_result2 !== undefined) {
        newSpeed = _result2;
      }
      return newSpeed;
    })
    

    SyncWaterfallHook的content 的onResult 与SyncBailHook相比多赋值的操作,判断是否有返回值,如果有返回值就将返回值传给下一个执行函数

    4.5 SyncLoopHook

    // content
    content({ onError, onDone, rethrowIfPossible }) {
    		return this.callTapsLooping({
    			onError: (i, err) => onError(err),
    			onDone,
    			rethrowIfPossible
    		});
    }
    //生成的函数
    (function anonymous() {
      "use strict";
       var _context;
       var _x = this._x;
       var _loop;
      do {
        _loop = false;
       var _fn0 = _x[0];
       var _result0 = _fn0();
       if(_result0 !== undefined) {     //根据返回值判断是否继续执行
         _loop = true;
       } else {
         var _fn1 = _x[1];
         var _result1 = _fn1();
         if(_result1 !== undefined) {
            _loop = true;
          } else {
           if(!_loop) {}
          }
       }
    } while(_loop);
    })
    
    
    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;
    	    },
    		....
    }
    

    注意 SyncLoopHook 的content 函数与上面三个不同,SyncLoopHook 的content 调用的是 callTapsLooping()函数,而其他的同步钩子调用的是 callTapsSeries(),

    4.6AsyncParallelHook

    // content函数
    content({ onError, onDone }) {
    		return this.callTapsParallel({
    			onError: (i, err, done, doneBreak) => onError(err) + doneBreak(true),
    			onDone
    		});
    }
    //生成的函数
    (function anonymous(_callback) {
        "use strict";
        var _context;
        var _x = this._x;
        do {
            var _counter = 2;
            var _done = () => {
                _callback();
            };
            if(_counter <= 0) break;
            var _fn0 = _x[0];
            _fn0(_err0 => {
                if(_err0) {                  //如果有参数传入
                    if(_counter > 0) {
                        _callback(_err0);
                        _counter = 0;
                    }
                } else {
                    if(--_counter === 0) _done();
                }
            });
            if(_counter <= 0) break;
            var _fn1 = _x[1];
            _fn1(_err1 => {
                if(_err1) {
                    if(_counter > 0) {
                        _callback(_err1);
                        _counter = 0;
                    }
                } else {
                    if(--_counter === 0) _done();
                }
            });
        } while(false);
    })
    

    根据AsyncParallelHook 钩子生成的函数我们可以看出他是判断err 有值时会走onError,执行 callback(),如果没有任何参数则当 _counter减为0即最后一个注册函数时执行callback()

    4.7AsyncParallelBailHook

    • // content
      content({ onError, onResult, onDone }) {
      		let code = "";
      		code += `var _results = new Array(${this.options.taps.length});\n`;
      		code += "var _checkDone = () => {\n";
      		code += "for(var i = 0; i < _results.length; i++) {\n";
      		code += "var item = _results[i];\n";
      		code += "if(item === undefined) return false;\n";
      		code += "if(item.result !== undefined) {\n";
      		code += onResult("item.result");
      		code += "return true;\n";
      		code += "}\n";
      		code += "if(item.error) {\n";
      		code += onError("item.error");
      		code += "return true;\n";
      		code += "}\n";
      		code += "}\n";
      		code += "return false;\n";
      		code += "}\n";
      		code += this.callTapsParallel({
      			onError: (i, err, done, doneBreak) => {
      				let code = "";
      				code += `if(${i} < _results.length && ((_results.length = ${i +
      					1}), (_results[${i}] = { error: ${err} }), _checkDone())) {\n`;
      				code += doneBreak(true);
      				code += "} else {\n";
      				code += done();
      				code += "}\n";
      				return code;
      			},
      			onResult: (i, result, done, doneBreak) => {
      				let code = "";
      				code += `if(${i} < _results.length && (${result} !== undefined && (_results.length = ${i +
      					1}), (_results[${i}] = { result: ${result} }), _checkDone())) {\n`;
      				code += doneBreak(true);
      				code += "} else {\n";
      				code += done();
      				code += "}\n";
      				return code;
      			},
      			onTap: (i, run, done, doneBreak) => {
      				let code = "";
      				if (i > 0) {
      					code += `if(${i} >= _results.length) {\n`;
      					code += done();
      					code += "} else {\n";
      				}
      				code += run();
      				if (i > 0) code += "}\n";
      				return code;
      			},
      			onDone
      		});
      		return code;
      }  
        
      //生成的函数
      (function anonymous(_callback
      ) {
          "use strict";
          var _context;
          var _x = this._x;
          var _results = new Array(2);
          var _checkDone = () => {
              for(var i = 0; i < _results.length; i++) {
                  var item = _results[i];
                  if(item === undefined) return false;
                  if(item.result !== undefined) {
                      _callback(null, item.result);
                      return true;
                  }
                  if(item.error) {
                      _callback(item.error);
                      return true;
                  }
              }
              return false;
          }
          do {
              var _counter = 2;
              var _done = () => {
                  _callback();
              };
              if(_counter <= 0) break;
              var _fn0 = _x[0];
              _fn0((_err0, _result0) => {
                  if(_err0) {
                      if(_counter > 0) {
                          if(0 < _results.length && ((_results.length = 1), (_results[0] = { error: _err0 }), _checkDone())) {
                              _counter = 0;
                          } else {
                              if(--_counter === 0) _done();
                          }
                      }
                  } else {
                      if(_counter > 0) {
                          if(0 < _results.length && (_result0 !== undefined && (_results.length = 1), (_results[0] = { result: _result0 }), _checkDone())) {
                              _counter = 0;
                          } else {
                              if(--_counter === 0) _done();
                          }
                      }
                  }
              });
              if(_counter <= 0) break;
              if(1 >= _results.length) {
                  if(--_counter === 0) _done();
              } else {
                  var _fn1 = _x[1];
                  _fn1((_err1, _result1) => {
                      console.log('_err',_err1)
                      if(_err1) {
                          if(_counter > 0) {
                              if(1 < _results.length && ((_results.length = 2), (_results[1] = { error: _err1 }), _checkDone())) {
                                  _counter = 0;
                              } else {
                                  if(--_counter === 0) _done();
                              }
                          }
                      } else {
                          if(_counter > 0) {
                              if(1 < _results.length && (_result1 !== undefined && (_results.length = 2), (_results[1] = { result: _result1 }), _checkDone())) {
                                  _counter = 0;
                              } else {
                                  if(--_counter === 0) _done();
                              }
                          }
                      }
                  });
              }
          } while(false);
        
      })
      

    网上有文章说这个钩子在第一个注册的插件执行完毕熔断,但好像不是这样,这个函数有个意思的地方,当err有参数传入时走的是 onError,没有参数走的是onResult

    callTap(tapIndex, { onError, onResult, onDone, rethrowIfPossible }) {
    	....
    	switch (tap.type) {
    			case "sync":
    			
    			case "async":
    				let cbCode = "";
    				if (onResult) cbCode += `(_err${tapIndex}, _result${tapIndex}) => {\n`;
    				else cbCode += `_err${tapIndex} => {\n`;
    				cbCode += `console.log('_err',_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":
    	       .....
    		}
    		return code;
    }
    
    这里判断err即有参数传入时执行的是onError,所以注册之后传入参数时其实执行的是_checkDone()循环判断_results的error有值时执行callback()
      if(_counter > 0) {
         if(0 < _results.length && ((_results.length = 1), (_results[0] = { error: _err0 }), _checkDone())) {
          _counter = 0;
         } else {
           if(--_counter === 0) _done();
          }
      }
    

    4.8AsyncSeriesHook

    //content 
    content({ onError, onDone }) {
    		return this.callTapsSeries({
    			onError: (i, err, next, doneBreak) => onError(err) + doneBreak(true),
    			onDone
    		});
    }
    //生成的函数
    (function anonymous(
    ) {
        "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;
            function _next0() {
                var _fn1 = _x[1];
                var _hasResult1 = false;
                var _promise1 = _fn1();
                if (!_promise1 || !_promise1.then)
                    throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise1 + ')');
                _promise1.then(_result1 => {
                    _hasResult1 = true;
                    _resolve();
                }, _err1 => {
                    if(_hasResult1) throw _err1;
                    _error(_err1);
                });
            }
            var _fn0 = _x[0];
            var _hasResult0 = false;
            var _promise0 = _fn0();
            if (!_promise0 || !_promise0.then)
                throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise0 + ')');
            _promise0.then(_result0 => {
                _hasResult0 = true;
                _next0();
            }, _err0 => {
                if(_hasResult0) throw _err0;
                _error(_err0);
            });
            _sync = false;
        });
    
    })
    

    AsyncSeriesHook 调用的是callTapsSeries 生成的执行函数,原理比较简单,就是当一个promise执行完之后再去执行另一个

    4.9AsyncSeriesBailHook

    // content
    content({ onError, onResult, resultReturns, onDone }) {
    		return this.callTapsSeries({
    			onError: (i, err, next, doneBreak) => onError(err) + doneBreak(true),
    			onResult: (i, result, next) =>
    				`if(${result} !== undefined) {\n${onResult(
    					result
    				)};\n} else {\n${next()}}\n`,
    			resultReturns,
    			onDone
    		});
    }
    //生成的函数
    (function anonymous() {
        "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;
            function _next0() {
                var _fn1 = _x[1];
                var _hasResult1 = false;
                var _promise1 = _fn1();
                if (!_promise1 || !_promise1.then)
                    throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise1 + ')');
                _promise1.then(_result1 => {
                    _hasResult1 = true;
                    if(_result1 !== undefined) {
                        _resolve(_result1);
                        ;
                    } else {
                        _resolve();
                    }
                }, _err1 => {
                    if(_hasResult1) throw _err1;
                    _error(_err1);
                });
            }
            var _fn0 = _x[0];
            var _hasResult0 = false;
            var _promise0 = _fn0();
            if (!_promise0 || !_promise0.then)
                throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise0 + ')');
            _promise0.then(_result0 => {
                _hasResult0 = true;
                if(_result0 !== undefined) {
                    _resolve(_result0);
                    ;
                } else {
                    _next0();
                }
            }, _err0 => {
                if(_hasResult0) throw _err0;
                _error(_err0);
            });
            _sync = false;
        });
    
    })
    
    

    这个钩子其实和SyncBailHook的实现是一样的,多一步判断返回值,如果有返回值就直接抛出了

    4.10AsyncSeriesWaterfallHook

    content({ onError, onResult, onDone }) {
    		return this.callTapsSeries({
    			onError: (i, err, next, doneBreak) => onError(err) + doneBreak(true),
    			onResult: (i, result, next) => {
    				let code = "";
    				code += `if(${result} !== undefined) {\n`;
    				code += `${this._args[0]} = ${result};\n`;
    				code += `}\n`;
    				code += next();
    				return code;
    			},
    			onDone: () => onResult(this._args[0])
    		});
    }
    //生成的函数
    (function anonymous(home
    ) {
        "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;
            function _next0() {
                var _fn1 = _x[1];
                var _hasResult1 = false;
                var _promise1 = _fn1(home);
                if (!_promise1 || !_promise1.then)
                    throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise1 + ')');
                _promise1.then(_result1 => {
                    _hasResult1 = true;
                    if(_result1 !== undefined) {
                        home = _result1;
                    }
                    _resolve(home);
                }, _err1 => {
                    if(_hasResult1) throw _err1;
                    _error(_err1);
                });
            }
            var _fn0 = _x[0];
            var _hasResult0 = false;
            var _promise0 = _fn0(home);
            if (!_promise0 || !_promise0.then)
                throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise0 + ')');
            _promise0.then(_result0 => {
                _hasResult0 = true;
                if(_result0 !== undefined) {
                    home = _result0;
                }
                _next0();
            }, _err0 => {
                if(_hasResult0) throw _err0;
                _error(_err0);
            });
            _sync = false;
        });
    
    })
    

    与SyncWaterfallHook 一样,多一步赋值过程


    起源地下载网 » webrpack之 tapable

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元