Webpack关键Tapable原理简析

187 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第12天,点击查看活动详情 

Tapable

tapable 是webpack核心,这个webpack运行包括hook机制都是tapable起作用

Tapable的使用

tap 注册 call调用,实际发布订阅模式大同小异,tap用来手机所有的回调,call用来执行回调方向。

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

const syncHook = new SyncHook()
syncHook.tap('logPlugin', () => { console.log('synchook 被执行了.')})
syncHook.call()

源码解析

new SyncHook() 新建对象

新建实例对象syncHookconst syncHook = new SyncHook() 主要是初始化作用,给hook所有要用到的变量赋默认值

constructor(args) {
      if (!Array.isArray(args)) args = [];
      this._args = args;
      this.taps = [];
      this.interceptors = [];
      this.call = this._call;
      this.promise = this._promise;
      this.callAsync = this._callAsync;
      this._x = undefined;
   }


  • 赋值给syncHoo实例 第二行代码syncHook.tap('logPlugin', () => { console.log('synchook 被执行了.')})

将传进去的参数变成 options: {type: "sync", fn: Function, name: "logPlugin"} 把option赋值给hook实例 ===> 最终执行 this.taps[i] = option;相当于tap收集起来所有的回调。

   tap(options, fn) {
      if (typeof options === "string") options = { name: options };
      if (typeof options !== "object" || options === null)
         throw new Error(
            "Invalid arguments to tap(options: Object, fn: function)"
         );
        // 将传进去的参数变成 options: {type: "sync", fn: Function, name: "logPlugin"}
      options = Object.assign({ type: "sync", fn: fn }, options);
      if (typeof options.name !== "string" || options.name === "")
         throw new Error("Missing name for tap");
        // 把option赋值给hook实例 ===> 最终执行 this.taps[i] = option;
        this._insert(options); 
   }
  • 生成执行函数并执行 第三行代码syncHook.call() 执行收集起来的回调函数
function createCompileDelegate(name, type) {
   return function lazyCompileHook(...args) {
      this[name] = this._createCall(type);
      return this[name](...args);
   };
}

_createCall(type) {
    return this.compile({
        taps: this.taps,
        interceptors: this.interceptors,
        args: this._args,
        type: type
    });
}

compile(options) {
    //给_x赋值,生成的匿名函数会用到
    factory.setup(this, options);
    // 生成匿名函数并返回
    return factory.create(options);
}

/**
 * 
 * 给 _x 赋值传进去的函数  () => { console.log('synchook 被执行了.')}
 */
*/
setup(instance, options) {
    instance._x = options.taps.map(t => t.fn);
}


/**
 * 
 *  hook关键方法,根据不同的hook生成不同的执行代码,
 *  此处为syncHook 最终生成的代码为  
 *  
 *  
  (function anonymous() {
    "use strict";
    var _context;
    var _x = this._x;
    var _fn0 = _x[0];
    _fn0();
})
 
 */
create(options) {
    this.init(options);
    let fn;
    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;
}

总结:主要是面试,面试时候回答原理的时候就是这么简单,同时消除恐惧不谈webpack色变。