.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);
        }
        // .....\node_modules\webpack\lib
        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比较水,没有什么内容。

转载于:https://www.cnblogs.com/QH-Jimmy/p/8179669.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值