Alexec'

安静的悲伤,安静的欢喜

你无法看到自己,你看到的是你认为的自己


Tapable.js 源码阅读与分析

function fastFilter(fun/*, thisArg*/) {
  'use strict';

  if (this === void 0 || this === null) {
    throw new TypeError();
  }

  var t = Object(this);
  var len = t.length >>> 0;
  if (typeof fun !== 'function') {
    throw new TypeError();
  }

  var res = [];
  var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
  for (var i = 0; i < len; i++) {
    if (i in t) {
      var val = t[i];

      // NOTE: Technically this should Object.defineProperty at
      //       the next index, as push can be affected by
      //       properties on Object.prototype and Array.prototype.
      //       But that method's new, and collisions should be
      //       rare, so use the more-compatible alternative.
      if (fun.call(thisArg, val, i, t)) {
        res.push(val);
      }
    }
  }

  return res;
}

function Tapable() {
  /* 存储事件 格式为
  {
    'emit': [() => {}, () => {}]
  }
   */
  this._plugins = {};
}
module.exports = Tapable;

// 属性拷贝
function copyProperties(from, to) {
  for(var key in from)
    to[key] = from[key];
  return to;
}

Tapable.mixin = function mixinTapable(pt) {
  copyProperties(Tapable.prototype, pt);
};

/**
 * 执行事件
 * 传入事件名称,事件多参数
 */
Tapable.prototype.applyPlugins = function applyPlugins(name) {
  if(!this._plugins[name]) return;
  var args = Array.prototype.slice.call(arguments, 1);
  var plugins = this._plugins[name];
  for(var i = 0; i < plugins.length; i++)
    plugins[i].apply(this, args);
};

/**
 * 执行事件
 * 传入事件名称,无参数
 */
Tapable.prototype.applyPlugins0 = function applyPlugins0(name) {
  var plugins = this._plugins[name];
  if(!plugins) return;
  for(var i = 0; i < plugins.length; i++)
    plugins[i].call(this);
};

/**
 * 执行事件
 * 传入事件名称,事件单参数
 */
Tapable.prototype.applyPlugins1 = function applyPlugins1(name, param) {
  var plugins = this._plugins[name];
  if(!plugins) return;
  for(var i = 0; i < plugins.length; i++)
    plugins[i].call(this, param);
};

/**
 * 执行事件
 * 传入事件名称,事件双参数
 */
Tapable.prototype.applyPlugins2 = function applyPlugins2(name, param1, param2) {
  var plugins = this._plugins[name];
  if(!plugins) return;
  for(var i = 0; i < plugins.length; i++)
    plugins[i].call(this, param1, param2);
};

/**
 * 执行事件
 * 传入事件名称,事件多参数
 * 执行后的返回值连带初始参数传入下一个事件
 * 返回最后一个事件执行结果
 */
Tapable.prototype.applyPluginsWaterfall = function applyPluginsWaterfall(name, init) {
  if(!this._plugins[name]) return init;
  var args = Array.prototype.slice.call(arguments, 2);
  var plugins = this._plugins[name];
  var current = init;
  for(var i = 0; i < plugins.length; i++)
    current = plugins[i].apply(this, [current].concat(args));
  return current;
};

/**
 * 执行事件
 * 传入事件名称,事件多参数
 * 第一个执行传入初始值
 * 执行后的返回值传入下一个事件
 * 返回最后一个事件执行结果
 */
Tapable.prototype.applyPluginsWaterfall0 = function applyPluginsWaterfall0(name, init) {
  var plugins = this._plugins[name];
  if(!plugins) return init;
  var current = init;
  for(var i = 0; i < plugins.length; i++)
    current = plugins[i].call(this, current);
  return current;
};

/**
 * 执行事件
 * 传入事件名称,事件多参数
 * 当事件有返回值时结束并返回,之后事件不执行
 */
Tapable.prototype.applyPluginsBailResult = function applyPluginsBailResult(name) {
  if(!this._plugins[name]) return;
  var args = Array.prototype.slice.call(arguments, 1);
  var plugins = this._plugins[name];
  for(var i = 0; i < plugins.length; i++) {
    var result = plugins[i].apply(this, args);
    if(typeof result !== "undefined") {
      return result;
    }
  }
};

/**
 * 执行事件
 * 传入事件名称,事件单参数
 * 当事件有返回值时结束并返回,之后事件不执行
 */
Tapable.prototype.applyPluginsBailResult1 = function applyPluginsBailResult1(name, param) {
  if(!this._plugins[name]) return;
  var plugins = this._plugins[name];
  for(var i = 0; i < plugins.length; i++) {
    var result = plugins[i].call(this, param);
    if(typeof result !== "undefined") {
      return result;
    }
  }
};

/**
 * 执行事件
 * 传入事件名称,事件多参数,最后一个参数为回调
 * 构造回调,在回调中执行下一个事件,事件执行完成后执行最初传入回调
 * 事件中要继续的话必须调用回调
 * 回调中的参数为 err ,有值时终止并调用最初传入回调
 */
Tapable.prototype.applyPluginsAsyncSeries = Tapable.prototype.applyPluginsAsync = function applyPluginsAsyncSeries(name) {
  var args = Array.prototype.slice.call(arguments, 1);
  var callback = args.pop();
  var plugins = this._plugins[name];
  if(!plugins || plugins.length === 0) return callback();
  var i = 0;
  var _this = this;
  args.push(copyProperties(callback, function next(err) {
    if(err) return callback(err);
    i++;
    if(i >= plugins.length) {
      return callback();
    }
    plugins[i].apply(_this, args);
  }));
  plugins[0].apply(this, args);
};

/**
 * 执行事件
 * 传入事件名称,事件三参数,最后一个参数为回调
 * 构造回调,在回调中执行下一个事件,事件执行完成后执行最初传入回调
 * 事件中要继续的话必须调用回调
 * 回调中的参数为 err ,有值时终止并调用最初传入回调
 * 无错时调用最初传入回调,无参
 */
Tapable.prototype.applyPluginsAsyncSeries1 = function applyPluginsAsyncSeries1(name, param, callback) {
  var plugins = this._plugins[name];
  if(!plugins || plugins.length === 0) return callback();
  var i = 0;
  var _this = this;
  var innerCallback = copyProperties(callback, function next(err) {
    if(err) return callback(err);
    i++;
    if(i >= plugins.length) {
      return callback();
    }
    plugins[i].call(_this, param, innerCallback);
  });
  plugins[0].call(this, param, innerCallback);
};

/**
 * 执行事件
 * 传入事件名称,事件多参数,最后一个参数为回调
 * 构造回调,在回调中执行下一个事件,事件执行完成后执行最初传入回调
 * 事件中要继续的话必须调用回调
 * 回调中传入多参数,有值时终止并调用最初传入回调
 */
Tapable.prototype.applyPluginsAsyncSeriesBailResult = function applyPluginsAsyncSeriesBailResult(name) {
  var args = Array.prototype.slice.call(arguments, 1);
  var callback = args.pop();
  if(!this._plugins[name] || this._plugins[name].length === 0) return callback();
  var plugins = this._plugins[name];
  var i = 0;
  var _this = this;
  args.push(copyProperties(callback, function next() {
    if(arguments.length > 0) return callback.apply(null, arguments);
    i++;
    if(i >= plugins.length) {
      return callback();
    }
    plugins[i].apply(_this, args);
  }));
  plugins[0].apply(this, args);
};

/**
 * 执行事件
 * 传入事件名称,事件多参数,最后一个参数为回调
 * 构造回调,在回调中执行下一个事件,事件执行完成后执行最初传入回调
 * 事件中要继续的话必须调用回调
 * 回调中的参数为 err, result,有值时终止并调用最初传入回调
 */
Tapable.prototype.applyPluginsAsyncSeriesBailResult1 = function applyPluginsAsyncSeriesBailResult1(name, param, callback) {
  var plugins = this._plugins[name];
  if(!plugins || plugins.length === 0) return callback();
  var i = 0;
  var _this = this;
  var innerCallback = copyProperties(callback, function next(err, result) {
    if(arguments.length > 0) return callback(err, result);
    i++;
    if(i >= plugins.length) {
      return callback();
    }
    plugins[i].call(_this, param, innerCallback);
  });
  plugins[0].call(this, param, innerCallback);
};

/**
 * 执行事件
 * 传入事件名称,事件三参数,最后一个参数为回调
 * 构造回调,在回调中执行下一个事件,事件执行完成后执行最初传入回调
 * 事件中要继续的话必须调用回调
 * 回调中的参数为 err, result,有值时终止并调用最初传入回调
 * 无错时调用最初传入回调,可传入最后一个事件的返回结果
 */
Tapable.prototype.applyPluginsAsyncWaterfall = function applyPluginsAsyncWaterfall(name, init, callback) {
  if(!this._plugins[name] || this._plugins[name].length === 0) return callback(null, init);
  var plugins = this._plugins[name];
  var i = 0;
  var _this = this;
  var next = copyProperties(callback, function(err, value) {
    if(err) return callback(err);
    i++;
    if(i >= plugins.length) {
      return callback(null, value);
    }
    plugins[i].call(_this, value, next);
  });
  plugins[0].call(this, init, next);
};

/**
 * 执行事件
 * @param name 传入事件名称,事件多参数,最后一个参数为回调
 * @returns {*}
 *
 * 构造回调,每次事件中可以多次调用回调
 * 某个事件多次调用 callback
 * 初始传入的 callback 会提前调用
 * 回调中的参数为 err, 有值时终止并调用最初传入回调
 */
Tapable.prototype.applyPluginsParallel = function applyPluginsParallel(name) {
  var args = Array.prototype.slice.call(arguments, 1);
  var callback = args.pop();
  if(!this._plugins[name] || this._plugins[name].length === 0) return callback();
  var plugins = this._plugins[name];
  var remaining = plugins.length;
  args.push(copyProperties(callback, function(err) {
    if(remaining < 0) return; // ignore
    if(err) {
      remaining = -1;
      return callback(err);
    }
    remaining--;
    if(remaining === 0) {
      return callback();
    }
  }));
  for(var i = 0; i < plugins.length; i++) {
    plugins[i].apply(this, args);
    if(remaining < 0) return;
  }
};

/**
 * 执行事件
 * @param name
 * @returns {*}
 * 每个事件都调用 callback 的话,才回调用初始传入的 callback
 * 某个事件多次调用 callback 初始传入的 callback 会提前调用 提前量为 callback.count - 3
 */
Tapable.prototype.applyPluginsParallelBailResult = function applyPluginsParallelBailResult(name) {
  var args = Array.prototype.slice.call(arguments, 1);
  var callback = args[args.length - 1];
  if(!this._plugins[name] || this._plugins[name].length === 0) return callback();
  var plugins = this._plugins[name];
  var currentPos = plugins.length;
  var currentResult;
  var done = [];
  for(var i = 0; i < plugins.length; i++) {
    args[args.length - 1] = (function(i) {
      return copyProperties(callback, function() {
        if(i >= currentPos) return; // ignore
        done.push(i);
        if(arguments.length > 0) {
          currentPos = i + 1;
          done = fastFilter.call(done, function(item) {
            return item <= i;
          });
          currentResult = Array.prototype.slice.call(arguments);
        }
        if(done.length === currentPos) {
          callback.apply(null, currentResult);
          currentPos = 0;
        }
      });
    }(i));
    plugins[i].apply(this, args);
  }
};

/**
 * 执行事件
 * @param name
 * @param param
 * @param callback
 * @returns {*}
 * 每个事件都调用 callback 的话,才回调用初始传入的 callback
 * 某个事件多次调用 callback 初始传入的 callback 会提前调用 提前量为 callback.count - 3
 */
Tapable.prototype.applyPluginsParallelBailResult1 = function applyPluginsParallelBailResult1(name, param, callback) {
  var plugins = this._plugins[name];
  if(!plugins || plugins.length === 0) return callback();
  var currentPos = plugins.length;
  var currentResult;
  var done = [];
  for(var i = 0; i < plugins.length; i++) {
    var innerCallback = (function(i) {
      return copyProperties(callback, function() {
        if(i >= currentPos) return; // ignore
        done.push(i);
        if(arguments.length > 0) {
          currentPos = i + 1;
          done = fastFilter.call(done, function(item) {
            return item <= i;
          });
          currentResult = Array.prototype.slice.call(arguments);
        }
        if(done.length === currentPos) {
          callback.apply(null, currentResult);
          currentPos = 0;
        }
      });
    }(i));
    plugins[i].call(this, param, innerCallback);
  }
};

/**
 * 注册事件
 * @param name 名称
 * @param fn 事件处理函数
 */
Tapable.prototype.plugin = function plugin(name, fn) {
  if(Array.isArray(name)) {
    name.forEach(function(name) {
      this.plugin(name, fn);
    }, this);
    return;
  }
  if(!this._plugins[name]) this._plugins[name] = [fn];
  else this._plugins[name].push(fn);
};

/**
 * 调用插件 apply 方法
 */
Tapable.prototype.apply = function apply() {
  for(var i = 0; i < arguments.length; i++) {
    arguments[i].apply(this);
  }
};

更早的文章

Hello World

hello world…

继续阅读