菜单

【澳门金沙国际】.八-浅析webpack源码之Tapable介绍

2019年5月9日 - 金沙前端

 基本操作函数

plugin

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);
};

  那是Tapable最中央的操作,给钦点的事件流注入新函数。

hasPlugins

Tapable.prototype.hasPlugins = function hasPlugins(name) {
    // 尝试获取对应事件流
    var plugins = this._plugins[name];
    // 存在事件流且有可执行函数
    return plugins && plugins.length > 0;
};

  has推断,没啥好讲的。

 

Tapable工具

  

  完毕webpack默许参数注入后,下一步即使是 new
Compiler() ,但是那东西不是仓卒之际得以讲完的,复杂的一群。

  比不上先从工具入手,分块疏解compiler,首先来探视事件流施行器Tapable工具。

澳门金沙国际,  tips:这里的Tapable源码来自于webpack内部自带的tapable,假诺因而npm
i tapable查看会发掘完全不壹致。

  出现地方如下:

class Compiler extends Tapable {
    // ...
}

class Compilation extends Tapable {
    // ...  
}

  能够阅览宗旨目的基本上都承接于该工具,用于处总管件流,Tapable源码整理如下(用ES六的class复写了2遍,看起来相比较清楚):

// 原型方法混入
Tapable.mixin = function mixinTapable(pt) { /**/ };

function copyProperties(from, to) { /**/ }

// 服务于某些apply
function fastFilter(fun /*, thisArg*/ ) { /*...*/ }

class Tapable {
    constructor() {
        this._plugins = {};
    }
    plugin(name, fn) { /*...*/ }
    hasPlugins(name) { /*...*/ }
    apply() { /*...*/ }
    applyPlugins(name) { /*...*/ }
    applyPlugins0(name) { /*...*/ }
    applyPlugins1(name, param) { /*...*/ }
    applyPlugins2(name, param1, param2) { /*...*/ }
    applyPluginsWaterfall(name, init) { /*...*/ }
    applyPluginsWaterfall0(name, init) { /*...*/ }
    applyPluginsWaterfall1(name, init, param) { /*...*/ }
    applyPluginsWaterfall2(name, init, param1, param2) { /*...*/ }
    applyPluginsBailResult(name) { /*...*/ }
    applyPluginsBailResult1(name, param) { /*...*/ }
    applyPluginsBailResult2(name, param1, param2) { /*...*/ }
    applyPluginsBailResult3(name, param1, param2, param3) { /*...*/ }
    applyPluginsBailResult4(name, param1, param2, param3, param4) { /*...*/ }
    applyPluginsBailResult5(name, param1, param2, param3, param4, param5) { /*...*/ }
    applyPluginsAsyncSeries(name) { /*...*/ }
    applyPluginsAsyncSeries1(name, param, callback) { /*...*/ }
    applyPluginsAsyncSeriesBailResult(name) { /*...*/ }
    applyPluginsAsyncSeriesBailResult1(name, param, callback) { /*...*/ }
    applyPluginsAsyncWaterfall(name, init, callback) { /*...*/ }
    applyPluginsParallel(name) { /*...*/ }
    applyPluginsParallelBailResult(name) { /*...*/ }
    applyPluginsParallelBailResult1(name, param, callback) { /*...*/ }
}

module.exports = Tapable;

  构造函数只是轻易的注明了3个_plugins对象,外部函数包涵有一个混入函数、三个工具函数,原型上则是大气apply…

  先从轻易的出手,看看混入函数:

// 将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);
};

  特别轻便,用一个小案例申明:

const Tapable = require('./Tapable');
var sourObj = { ownKey: null };
Tapable.mixin(sourObj);

  通过mixin方法的调用,sourObj会成为:

澳门金沙国际 1

  至于别的叁个工具函数,单独讲未有任何意义,所以在选用的时候再做深入分析。

  

  接下去剖判原型函数,个中有五个函数是基本操作函数,别的的都以用不一样方法进行钦赐名字的风波流。

  先看基本的。

 

事件流推行

  接下去看看全体的风云流推行措施。(源码中尽量选择ES6进行改写以抓好可读性,留个注释在那)

  首先是三个相比新鲜的原型函数:

apply

Tapable.prototype.apply = function apply(...fns) {
    // 遍历所有参数并执行
    for (var i = 0; i < fns.length; i++) {
        fns[i].apply(this);
    }
};

  该函数并不间接关联于_plugins对象,而是遵从参数传入顺序依次实行。

 

applyPlugins

  那个办法特别简单暴力,依次遍历钦命name的轩然大波流,差别名字的函数可接受参数数量不等同。

// 不接受传参
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.applyPlugins = function applyPlugins(name, ...args) {
    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);
};

  语义化满分,0代表不收受参数,1代表二个…而s代表私自数量的参数。

 

applyPluginsWaterfall

  这种措施的特色是:事件流施行进程中,每2次施行的归来值会作为下三遍的参数(只限于第三个参数)。

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;
};

// ...1
// ...2

Tapable.prototype.applyPluginsWaterfall = function applyPluginsWaterfall(name, init, ...args) {
    if (!this._plugins[name]) return init;
    // var args = Array.prototype.slice.call(arguments, 1);
    var plugins = this._plugins[name];
    var current = init;
    for (var i = 0; i < plugins.length; i++) {
        current = plugins[i].call(this, current, ...args);
    }
    return current;
};

 

applyPluginsBailResult

  这种艺术的天性是:事件流试行进度中,重临首个不是undefined的值,后续函数不施行。

Tapable.prototype.applyPluginsBailResult = function applyPluginsBailResult(name, ...args) {
    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;
        }
    }
};

// 1,2,3,4,5

 

applyPluginsAsync…

  带有Async的均为异步调用形式,特点是事件流会在回调中相继张开,差距紧要在于回调函数的参数管理,具体的利用办法还索要在骨子里行使中来看。

Tapable.prototype.applyPluginsAsyncSeries = Tapable.prototype.applyPluginsAsync = function applyPluginsAsyncSeries(name, ...args) {
    // 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, (err) => {
        if (err) return callback(err);
        i++;
        if (i >= plugins.length) {
            return callback();
        }
        plugins[i].apply(this, args);
    }));
    // 内部继续使用此方式可依次执行事件流
    plugins[0].apply(this, args);
};

// ..1

// applyPluginsAsyncSeriesBailResult => 回调函数传了参数就直接执行回调并返回终止事件流

// ..1

// applyPluginsAsyncWaterfall => 回调函数每次取给定的参数

  剩下的1个相比复杂,干讲也不知情怎么解释,等到前面包车型大巴代码有使用的时候再组具体分析。

  

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图