澳门网络娱乐游戏平台-澳门电子游戏娱乐网址-官方直营

.23-浅析webpack源码之事件流compilation(1)

  正式开班跑编写翻译,依次拆解深入分析,首先是:

.23-浅析webpack源码之事件流compilation(1)。  这里具备的插件都对应着一个小成效,画个图收拾下前段时间流程:

compiler.apply(
    new JsonpTemplatePlugin(options.output),
    // start
    new FunctionModulePlugin(options.output),
    new NodeSourcePlugin(options.node),
    new LoaderTargetPlugin(options.target)
);

澳门网站网址大全 1

  流程图如下:

  上节是从ParsePlugin中出来,对'./input.js'入口文件的路径做了拍卖,重回如下:

澳门网站网址大全 2

ParsePlugin.prototype.apply = function(resolver) {
    var target = this.target;
    resolver.plugin(this.source, function(request, callback) {
        // 分析request是否为模块或文件夹
        var parsed = resolver.parse(request.request);
        var obj = Object.assign({}, request, parsed);
        if (request.query && !parsed.query) {
            obj.query = request.query;
        }
        if (parsed && callback.log) {
            if (parsed.module)
                callback.log("Parsed request is a module");
            if (parsed.directory)
                callback.log("Parsed request is a directory");
        }
        // 拼接后的obj如下
        /*
            {
                context: { issuer: '', compiler: undefined },
                path: 'd:\workspace\doc',
                request: './input.js',
                query: '',
                module: false,
                directory: false,
                file: false
            }
        */
        // target => parsed-resolve
        resolver.doResolve(target, obj, null, callback);
    });
};

  这里是率先个compilation事件注入之处,注入代码如下:

  该插件调用完后,步向下三个事变流,开首跑跑parsed-resolve相关的了。

compiler.plugin("compilation", (compilation) => {
    compilation.moduleTemplate.requestShortener = this.requestShortener || new RequestShortener(compiler.context);
    compilation.moduleTemplate.apply(new FunctionModuleTemplatePlugin());
});

  回头看了一眼28节的大流程图,发掘好些个这个事件流都是串联起来挨门逐户注入的,幸亏不用本人去找在哪了。

  这里的requestShortener为FunctionModulePlugin的第二个参数,未有传所以是undefined。

  

  options.output为流传的output参数,可是此间并从未运用,而是传入了compiler.context,若无传默以为命令实践路线。

createInnerCallback

  

  这里先讲一下早先跳过的回调函数生成器,在Resolver中调用如下:

RequestShortener

// before-callback
createInnerCallback(beforeInnerCallback, {
    log: callback.log,
    missing: callback.missing,
    stack: newStack
}, message && ("before " + message), true);
// normal-callback
createInnerCallback(innerCallback, {
    log: callback.log,
    missing: callback.missing,
    stack: newStack
}, message);
// after-callback
createInnerCallback(afterInnerCallback, {
    log: callback.log,
    missing: callback.missing,
    stack: newStack
}, message && ("after " + message), true);

  首先看率先个,源码简化如下:

  方法的第四个参数都毫无二致,取第三个为例:

"use strict";

const path = require("path");
// 匹配反斜杠 => 
const NORMALIZE_SLASH_DIRECTION_REGEXP = /\/g;
// 匹配特殊字符
const PATH_CHARS_REGEXP = /[-[]{}()*+?.,\^$|#s]/g;
// 匹配正反斜杠 => /
const SEPARATOR_REGEXP = /[/\]$/;
// 匹配以'!'开头或结尾
const FRONT_OR_BACK_BANG_REGEXP = /^!|!$/g;
// 匹配 /index.js
const INDEX_JS_REGEXP = //index.js(!|?|(query))/g;
// 将反斜杠替换为正斜杠
const normalizeBackSlashDirection = (request) => {
    return request.replace(NORMALIZE_SLASH_DIRECTION_REGEXP, "/");
};
// 将路径中特殊字符转义 例如 - => -
// 返回一个正则
const createRegExpForPath = (path) => {
    const regexpTypePartial = path.replace(PATH_CHARS_REGEXP, "\$&");
    return new RegExp(`(^|!)${regexpTypePartial}`, "g");
};

class RequestShortener {
    constructor(directory) { /**/ }
    shorten(request) { /**/ }
}

module.exports = RequestShortener;
function beforeInnerCallback(err, result) {
    // 根据调用callback时是否有参数决定调用回调函数还是进入下一阶段
    if (arguments.length > 0) {
        if (err) return callback(err);
        if (result) return callback(null, result);
        return callback();
    }
    runNormal();
}

  能够观察都以对路线做管理,正则都比较容易,接下去看一下构造函数,在那之中传进来的directory为命令施行上下文。

  剩下的五个也只是把runNormal形成了runAfter与callback而已。

class RequestShortener {
    constructor(directory) {
        // 斜杠转换
        directory = normalizeBackSlashDirection(directory);
        // 没看懂啥用
        if (SEPARATOR_REGEXP.test(directory)) directory = directory.substr(0, directory.length - 1);
        // 上下文路径正则
        // /(^|!)转义后的路径/g
        if (directory) {
            this.currentDirectoryRegExp = createRegExpForPath(directory);
        }
        // 返回目录名
        const dirname = path.dirname(directory);
        // 这里也不懂干啥用的
        const endsWithSeperator = SEPARATOR_REGEXP.test(dirname);
        const parentDirectory = endsWithSeperator ? dirname.substr(0, dirname.length - 1) : dirname;
        // 目录正则
        if (parentDirectory && parentDirectory !== directory) {
            this.parentDirectoryRegExp = createRegExpForPath(parentDirectory);
        }
        // .....node_moduleswebpacklib
        if (__dirname.length >= 2) {
            // webpack的目录
            const buildins = normalizeBackSlashDirection(path.join(__dirname, ".."));
            // 目录检测
            const buildinsAsModule = this.currentDirectoryRegExp && this.currentDirectoryRegExp.test(buildins);
            // false
            this.buildinsAsModule = buildinsAsModule;
            // 生成webpack目录路径正则
            this.buildinsRegExp = createRegExpForPath(buildins);
        }
    }
    shorten(request) { /**/ }
}

  有了参数,接下去看一下生成器的内部得以达成:

  重借使生成了3个目录相配正则,上下文、上下文目录、webpack主目录三个。

module.exports = function createInnerCallback(callback, options, message, messageOptional) {
    var log = options.log;
    // 无log时
    if (!log) {
        // 基本上也是返回callback
        // 只是把options的两个方法挂载上去了
        if (options.stack !== callback.stack) {
            var callbackWrapper = function callbackWrapper() {
                return callback.apply(this, arguments);
            };
            callbackWrapper.stack = options.stack;
            callbackWrapper.missing = options.missing;
            return callbackWrapper;
        }
        return callback;
    }
    // 这个方法是批量取出本地log数组的内容然后调用options的log方法
    function loggingCallbackWrapper() {
        var i;
        if (message) {
            if (!messageOptional || theLog.length > 0) {
                log(message);
                for (i = 0; i < theLog.length; i++)
                    log("  " + theLog[i]);
            }
        } else {
            for (i = 0; i < theLog.length; i++)
                log(theLog[i]);
        }
        return callback.apply(this, arguments);

    }
    // 有log时
    var theLog = [];
    loggingCallbackWrapper.log = function writeLog(msg) {
        theLog.push(msg);
    };
    loggingCallbackWrapper.stack = options.stack;
    loggingCallbackWrapper.missing = options.missing;
    return loggingCallbackWrapper;
};

  这里上下文平常不会是webpack的目录,所以那些buildingsAsModule理论上都是flase。

  这里的log当先风华正茂全场所下都以undefined,所以不经常能够感到再次回到的大半是率先个参数callback自个儿。

  再轻易看一下原型方法shorten:

  有log时也不复杂,等传播的options自带有效log时再看。

class RequestShortener {
    constructor(directory) { /**/ }
    shorten(request) {
        if (!request) return request;
        // 转化路径斜杠
        request = normalizeBackSlashDirection(request);
        // false
        if (this.buildinsAsModule && this.buildinsRegExp)
            request = request.replace(this.buildinsRegExp, "!(webpack)");
        // 将上下文转换为!.
        if (this.currentDirectoryRegExp)
            request = request.replace(this.currentDirectoryRegExp, "!.");
        // 将上下文目录转换为!..
        if (this.parentDirectoryRegExp)
            request = request.replace(this.parentDirectoryRegExp, "!..");
        // false
        if (!this.buildinsAsModule && this.buildinsRegExp)
            request = request.replace(this.buildinsRegExp, "!(webpack)");
        // 把路径中的index.js去了 留下参数
        // /index.js?a=1 => ?a=1
        request = request.replace(INDEX_JS_REGEXP, "$1");
        // 把头尾的!去了
        return request.replace(FRONT_OR_BACK_BANG_REGEXP, "");
    }
}

 

  可以阅览,那一个方法将盛传的路线根据上下文的目录实行简化,产生了相对路径,然后去掉了index.js。

澳门网站网址大全,DescriptionFilePlugin

 

  继续跑流程,这几个插件正是对package.json配置文件进行拆解解析,源码简化如下:

24小时娱乐5522400,FunctionModuleTemplatePlugin

// request => 之前的obj
// callback => createInnerCallback(...)
(request, callback) => {
    const directory = request.path;
    /*
        resolver => 大对象
        directory => 'd:\workspace\doc'
        filenames => ['package.json']
    */
    DescriptionFileUtils.loadDescriptionFile(resolver, directory, filenames, ((err, result) => { /**/ }));
};

  这么些模块未有实质性内容,首倘诺对compilation.moduleTemplate注入事件流,源码如下:

  这里直接在在那之中调用了此外一个工具类的实例方法,源码如下:

"use strict";

const ConcatSource = require("webpack-sources").ConcatSource;

class FunctionModuleTemplatePlugin {
    apply(moduleTemplate) {
        moduleTemplate.plugin("render", function(moduleSource, module) { /**/ });
        moduleTemplate.plugin("package", function(moduleSource, module) { /**/ });
        moduleTemplate.plugin("hash", function(hash) { /**/ });
    }
}
module.exports = FunctionModuleTemplatePlugin;
var forEachBail = require("./forEachBail");

function loadDescriptionFile(resolver, directory, filenames, callback) {
    (function findDescriptionFile() {
        forEachBail(filenames, function(filename, callback) { /**/ }, function(err, result) { /**/ });
    }());
}

  等触发的时候再回头看。

 

  ConcatSource后边单独讲。

forEachBail

 

  内部引用了一个工具方法做迭代,继续看:

  上边是第二个插件,源码收拾如下:

// 参数名字说明一切
module.exports = function forEachBail(array, iterator, callback) {
    if (array.length === 0) return callback();
    var currentPos = array.length;
    var currentResult;
    var done = [];
    for (var i = 0; i < array.length; i++) {
        var itCb = createIteratorCallback(i);
        // 传入数组元素与生成的迭代器回调函数
        iterator(array[i], itCb);
        if (currentPos === 0) break;
    }

    function createIteratorCallback(i) {
        return function() {
            if (i >= currentPos) return; // ignore
            var args = Array.prototype.slice.call(arguments);
            done.push(i);
            if (args.length > 0) {
                currentPos = i + 1;
                done = done.filter(function(item) {
                    return item <= i;
                });
                // 将该回调的参数赋值到外部变量
                currentResult = args;
            }
            // 遍历完调用callback
            if (done.length === currentPos) {
                callback.apply(null, currentResult);
                currentPos = 0;
            }
        };
    }
};

本文由澳门网络娱乐游戏平台发布于Web前端,转载请注明出处:.23-浅析webpack源码之事件流compilation(1)

相关阅读