zoukankan      html  css  js  c++  java
  • .28-浅析webpack源码之compiler.resolvers

      原本该在过WebpackOptionsApply时讲解这个方法的,但是当时一不小心过掉了,所以在这里补上。

    compiler.resolvers

      该对象的三个方法均在WebpackOptionsApply中生成,代码如下:

    compiler.resolvers.normal = ResolverFactory.createResolver(Object.assign({
        fileSystem: compiler.inputFileSystem
    }, options.resolve));
    compiler.resolvers.context = ResolverFactory.createResolver(Object.assign({
        fileSystem: compiler.inputFileSystem,
        resolveToContext: true
    }, options.resolve));
    compiler.resolvers.loader = ResolverFactory.createResolver(Object.assign({
        fileSystem: compiler.inputFileSystem
    }, options.resolveLoader));

      由于调用的是一个工厂函数,所以用normal作为示例讲解。

      

    /*
        "resolve": {
            "unsafeCache": true,
            "modules": ["node_modules"],
            "extensions": [".js", ".json"],
            "mainFiles": ["index"],
            "aliasFields": ["browser"],
            "mainFields": ["browser", "module", "main"],
            "cacheWithContext": false
        },
    */
    compiler.resolvers.normal = ResolverFactory.createResolver(Object.assign({
        fileSystem: compiler.inputFileSystem
    }, options.resolve));

      其中参数中的resolve取了默认值,如注释所示。

    ResolveFactory.createResolver

      这个方法比较有意思,一块一块的来看源码,所有注释保留英文原文更好理解:

    exports.createResolver = function(options) {
        //// OPTIONS ////
    
        // A list of directories to resolve modules from, can be absolute path or folder name
        // 模块文件夹的目录或者文件夹名称
        var modules = options.modules || ["node_modules"];
    
        // A list of description files to read from
        // 描述配置文件名
        var descriptionFiles = options.descriptionFiles || ["package.json"];
    
        // A list of additional resolve plugins which should be applied
        // The slice is there to create a copy, because otherwise pushing into plugins
        // changes the original options.plugins array, causing duplicate plugins
        // 额外的插件
        var plugins = (options.plugins && options.plugins.slice()) || [];
    
        // A list of main fields in description files
        // 不知道干啥的
        var mainFields = options.mainFields || ["main"];
    
        // A list of alias fields in description files
        // 不知道干啥的
        var aliasFields = options.aliasFields || [];
    
        // A list of main files in directories
        // 模块主入口文件名
        var mainFiles = options.mainFiles || ["index"];
    
        // A list of extensions which should be tried for files
        // 默认的文件扩展名
        var extensions = options.extensions || [".js", ".json", ".node"];
    
        // Enforce that a extension from extensions must be used
        var enforceExtension = options.enforceExtension || false;
    
        // A list of module extensions which should be tried for modules
        var moduleExtensions = options.moduleExtensions || [];
    
        // Enforce that a extension from moduleExtensions must be used
        var enforceModuleExtension = options.enforceModuleExtension || false;
    
        // A list of module alias configurations or an object which maps key to value
        // 别名
        var alias = options.alias || [];
    
        // ...还有一些其他奇奇怪怪的属性
    
        //// options processing ////
    
        // ...第二部分
    };

      这一步是包装参数,主要看注释,基本上对resolve参数下的各个key都做了解释,有一些实在不知道干啥用的就省略了。

      基本上可能会自定义的大概只有extensions、alias两个属性。

      下面来看第二部分:

    exports.createResolver = function(options) {
        //// OPTIONS ////
    
        // ...第一部分
    
        //// options processing ////
    
        if (!resolver) {
            // useSyncFileSystemCalls默认为undefined
            resolver = new Resolver(useSyncFileSystemCalls ? new SyncAsyncFileSystemDecorator(fileSystem) : fileSystem);
        }
        // 数组包装
        extensions = [].concat(extensions);
        moduleExtensions = [].concat(moduleExtensions);
        // 返回[['node_modules']]
        modules = mergeFilteredToArray([].concat(modules), function(item) {
            return !isAbsolutePath(item);
        });
        // 不懂这个参数干啥的
        // 返回一个对象数组
        mainFields = mainFields.map(function(item) {
            if (typeof item === "string") {
                item = {
                    name: item,
                    forceRelative: true
                };
            }
            return item;
        });
        // 处理别名
        if (typeof alias === "object" && !Array.isArray(alias)) { /**/ }
    
        // 不知道什么东西
        if (unsafeCache && typeof unsafeCache !== "object") {
            unsafeCache = {};
        }
    
        //// pipeline ////
    
        // ...第三部分
    };

      这一部分是处理参数,resolver是最后返回的对象,到调用的时候再细看。

      几个参数由于不太懂什么作用,处理方法也很简单,就不做解释,这里看一下alias别名的处理:

    /*
        alias: {
            'vue$': 'vue/dist/vue.esm.js',
            '@': '../src'
        }
    */
    /*
        alias:[
            {
                name: 'vue',
                onlyModule: true,
                alias: 'vue/dist/vue.esm.js'
            },
            {
                name: '@',
                onlyModule: false,
                alias: '../src'           
            }
        ]
    */
    if (typeof alias === "object" && !Array.isArray(alias)) {
        alias = Object.keys(alias).map(function(key) {
            var onlyModule = false;
            var obj = alias[key];
            // 测试是否以$结尾
            if (/$$/.test(key)) {
                onlyModule = true;
                key = key.substr(0, key.length - 1);
            }
            // alias的值是否为字符串
            if (typeof obj === "string") {
                obj = {
                    alias: obj
                };
            }
            obj = Object.assign({
                name: key,
                onlyModule: onlyModule
            }, obj);
            return obj;
        });
    }

      这里以vue-cli为例,展示了转换后的alias,看注释就OK了。

      

      第三部分有点恶心,源码大概是这样子的:

    exports.createResolver = function(options) {
        //// OPTIONS ////
    
        // ...第一部分
    
        //// options processing ////
    
        // ...第二部分
    
        //// pipeline ////
    
        // resolve
        if (unsafeCache) {
            plugins.push(new UnsafeCachePlugin("resolve", cachePredicate, unsafeCache, cacheWithContext, "new-resolve"));
            plugins.push(new ParsePlugin("new-resolve", "parsed-resolve"));
        } else {
            plugins.push(new ParsePlugin("resolve", "parsed-resolve"));
        }
    
        // ...无穷多的if + plugins.push(...)
    
        //// RESOLVER ////
    
        plugins.forEach(function(plugin) {
            resolver.apply(plugin);
        });
        return resolver;
    };

      虽然有非常多的plugin,但是内部处理形式大同小异。所以就一个常用参数作为例子,比如说:

    // described-resolve
    alias.forEach(function(item) {
        plugins.push(new AliasPlugin("described-resolve", item, "resolve"));
    });

      简要的看一下内部,这里的alias就是上面转换后的对象数组。

    class AliasPlugin {
        constructor(source, options, target) {
            this.source = source;
            this.name = options.name;
            this.alias = options.alias;
            this.onlyModule = options.onlyModule;
            this.target = target;
        }
        apply(resolver) {
            var target = this.target;
            var name = this.name;
            var alias = this.alias;
            var onlyModule = this.onlyModule;
            resolver.plugin(this.source, function(request, callback) { /**/ });
        }
    }

      第三部分所有的plugins都是这样的形式。

    1、构造函数仅仅获取并初始化值

    2、有一个apply方法,接受一个resolver参数

    3、注入参数source的事件流

    4、在source事件流最后,target参数会在resolver.doResolve方法中被调用,这里省略了代码

      在函数的最后,可以看到有一个这样的调用:

    plugins.forEach(function(plugin) {
        resolver.apply(plugin);
    });

      这里就是依次执行所有plugin的apply方法,传入resolver作为参数。

      所有的插件plugin流程以示意图的形式给出,这里就不一一分析了。

     

      注入完所有的事件流后,返回这个resolver对象,也就是compiler.resolvers.normal(loader、context)。

  • 相关阅读:
    知道这几 个正则表达式,能让你少写 1,000 行代码
    移除手机端a标签点击自动出现的边框和背景
    CSS 元素垂直居中的 6种方法
    当文本超出时出现省略号
    css清除select的下拉箭头样式
    设置透明边框
    js 输出语句document.write()及动态改变元素中内容innerHTML的使用
    LOCAL_EXPORT_××用法
    sprd测试系统跑vts
    C++ const用法
  • 原文地址:https://www.cnblogs.com/QH-Jimmy/p/8260189.html
Copyright © 2011-2022 走看看