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


      正式开始跑编译,依次解析,首先是:

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

      流程图如下:

      这里是第一个compilation事件注入的地方,注入代码如下:

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

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

      options.output为传入的output参数,但是这里并没有用到,而是传入了compiler.context,如果没有传默认为命令执行路径。

      

    RequestShortener

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

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

      可以看到都是对路径做处理,正则都比较简单,接下来看一下构造函数,其中传进来的directory为命令执行上下文。

    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);
            }
            // .....
    ode_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主目录三个。

      这里上下文一般不会是webpack的目录,所以这个buildingsAsModule理论上都是flase。

      再简单看一下原型方法shorten:

    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。

    FunctionModuleTemplatePlugin

      这个模块没有实质性内容,主要是对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;

      等触发的时候再回头看。

      ConcatSource后面单独讲。

      下面是第二个插件,源码整理如下:

    class NodeSourcePlugin {
        constructor(options) {
            this.options = options;
        }
        apply(compiler) {
            const options = this.options;
            if (options === false) // allow single kill switch to turn off this plugin
                return;
    
            function getPathToModule(module, type) { /**/ }
    
            function addExpression(parser, name, module, type, suffix) { /**/ }
            compiler.plugin("compilation", function(compilation, params) {
                params.normalModuleFactory.plugin("parser", function(parser, parserOptions) { /**/ });
            });
            compiler.plugin("after-resolvers", (compiler) => { /**/ });
        }
    };

      可以看到,这里只是简单判断了是否关闭了node插件,然后在之前的params参数中的normalModuleFactory属性上注入了一个parser事件。

      第三个插件就更简单了,如下:

    class LoaderTargetPlugin {
        constructor(target) {
            this.target = target;
        }
        apply(compiler) {
            compiler.plugin("compilation", (compilation) => {
                // 这个完全不懂干啥的
                compilation.plugin("normal-module-loader", (loaderContext) => loaderContext.target = this.target);
            });
        }
    }

      这个plugin目前根本看不出来有什么用。

      总之,前三个compilation比较水,没有什么内容。

  • 相关阅读:
    Excel导出到浏览器(个人备份)
    关于js性能
    关于JQuery(最后一点动画效果*)
    Python 2.7 学习笔记 内置语句、函数、标准库
    Python 2.7 学习笔记 模块和包
    Python 2.7 学习笔记 异常处理
    Python 2.7 学习笔记 字典(map)的使用
    Python 2.7 学习笔记 元组的使用
    Python 2.7 学习笔记 列表的使用
    Python 2.7 学习笔记 条件与循环语句
  • 原文地址:https://www.cnblogs.com/QH-Jimmy/p/8179669.html
Copyright © 2020-2023  润新知