zoukankan      html  css  js  c++  java
  • .37-浅析webpack源码之事件流make(4)

      赶紧完结这个系列咯,webpack4都已经出正式版了。

      之前的代码搜索到js文件的对应loader,并添加到了对象中返回,流程如下:

    this.plugin("factory", () => (result, callback) => {
        let resolver = this.applyPluginsWaterfall0("resolver", null);
    
        // Ignored
        if (!resolver) return callback();
    
        // 这里的data就是返回的大对象
        /*
            data => 
            {
                context: 'd:\workspace\doc',
                request: 'd:\workspace\node_modules\babel-loader\lib\index.js!d:\workspace\doc\input.js',
                dependencies: [SingleEntryDependency],,
                userRequest: 'd:\workspace\doc\input.js',
                rawRequest: './input.js',
                loaders: [ { loader: 'd:\workspace\node_modules\babel-loader\lib\index.js' } ],
                resource: 'd:\workspace\doc\input.js',
                还有package.json与parser相关的属性
            }
        */
        resolver(result, (err, data) => {
            if (err) return callback(err);
    
            // Ignored
            if (!data) return callback();
    
            // direct module
            if (typeof data.source === "function")
                return callback(null, data);
    
            this.applyPluginsAsyncWaterfall("after-resolve", data, (err, result) => {
                // ...
            })
        })
    })

      这个对象的request将入口文件的路径与loader拼接起来并用!分割,所有的属性基本上都与路径相关。

    after-resolve事件流

      这里会触发after-resolve事件流,注入地点如下:

    const matchJson = /.json$/i;
    params.normalModuleFactory.plugin("after-resolve", (data, done) => {
        // if this is a json file and there are no loaders active, we use the json-loader in order to avoid parse errors
        // @see https://github.com/webpack/webpack/issues/3363
        if (matchJson.test(data.request) && data.loaders.length === 0) {
            data.loaders.push({
                loader: jsonLoaderPath
            });
        }
        done(null, data);
    });

      注释已经写的很明白了,这里是检测待处理文件类型是否是json文件,如果是并且没有对应的loader,就将内置的json-loader作为loader。

    callback

      接下来看回调函数内容:

    this.applyPluginsAsyncWaterfall("after-resolve", data, (err, result) => {
        if (err) return callback(err);
    
        // Ignored
        if (!result) return callback();
        // 无此事件流 返回undefined
        let createdModule = this.applyPluginsBailResult("create-module", result);
        if (!createdModule) {
    
            if (!result.request) {
                return callback(new Error("Empty dependency (no request)"));
            }
            // 创建
            createdModule = new NormalModule(
                result.request,
                result.userRequest,
                result.rawRequest,
                result.loaders,
                result.resource,
                result.parser
            );
        }
        // 无此事件流
        createdModule = this.applyPluginsWaterfall0("module", createdModule);
    
        return callback(null, createdModule);
    });

      这里的两个事件流都是没有的,所以只需要看那个创建类的过程,然后就直接返回了。

      只看下构造函数:

    class NormalModule extends Module {
        /* 
            request => 'd:\workspace\node_modules\babel-loader\lib\index.js!d:\workspace\doc\input.js'
            userRequest => 'd:\workspace\doc\input.js'
            rawRequest => './input.js'
            loaders => [ { loader: 'd:\workspace\node_modules\babel-loader\lib\index.js' } ]
            resource => 'd:\workspace\doc\input.js'
            parser => [Parser]
        */
        constructor(request, userRequest, rawRequest, loaders, resource, parser) {
            super();
            this.request = request;
            this.userRequest = userRequest;
            this.rawRequest = rawRequest;
            this.parser = parser;
            this.resource = resource;
            this.context = getContext(resource);
            this.loaders = loaders;
            this.fileDependencies = [];
            this.contextDependencies = [];
            this.warnings = [];
            this.errors = [];
            this.error = null;
            this._source = null;
            this.assets = {};
            this.built = false;
            this._cachedSource = null;
        }
    
        // ...原型方法
    }

      只有一个getContext方法是执行的,这个方法比较简单,过一下就行:

    exports.getContext = function getContext(resource) {
        var splitted = splitQuery(resource);
        return dirname(splitted[0]);
    };
    
    // 切割参数
    function splitQuery(req) {
        var i = req.indexOf("?");
        if (i < 0) return [req, ""];
        return [req.substr(0, i), req.substr(i)];
    }
    
    // 返回目录
    // d:\workspace\doc\input.js => d:\workspace\doc(反斜杠这里有转义)
    function dirname(path) {
        if (path === "/") return "/";
        var i = path.lastIndexOf("/");
        var j = path.lastIndexOf("\");
        var i2 = path.indexOf("/");
        var j2 = path.indexOf("\");
        var idx = i > j ? i : j;
        var idx2 = i > j ? i2 : j2;
        if (idx < 0) return path;
        if (idx === idx2) return path.substr(0, idx + 1);
        return path.substr(0, idx);
    }

      返回了入口文件路径的目录,也就是上下文。

      这样一路回调返回,回到了最初的原型方法create:

    // NormalModuleFactory
    const factory = this.applyPluginsWaterfall0("factory", null);
    
    // Ignored
    if (!factory) return callback();
    
    factory(result, (err, module) => {
        // 这里的module就是new出来的NormalModule对象
        if (err) return callback(err);
        // this.cachePredicate = typeof options.unsafeCache === "function" ? options.unsafeCache : Boolean.bind(null, options.unsafeCache);
        // 由于默认情况下unsafeCache为true 所以这个函数默认一直返回true
        if (module && this.cachePredicate(module)) {
            dependencies.forEach(d => d.__NormalModuleFactoryCache = module);
        }
    
        callback(null, module);
    });

      这里对之前处理的入口文件对象做了缓存。

      返回又返回,回到了Compilation对象中:

    _addModuleChain(context, dependency, onModule, callback) {
        // ...
    
        this.semaphore.acquire(() => {
            // 从这里出来
            moduleFactory.create({
                contextInfo: {
                    issuer: "",
                    compiler: this.compiler.name
                },
                context: context,
                dependencies: [dependency]
            }, (err, module) => {
                if (err) {
                    this.semaphore.release();
                    return errorAndCallback(new EntryModuleNotFoundError(err));
                }
    
                let afterFactory;
                // 不存在的属性
                if (this.profile) {
                    if (!module.profile) {
                        module.profile = {};
                    }
                    afterFactory = Date.now();
                    module.profile.factory = afterFactory - start;
                }
    
                const result = this.addModule(module);
                // ...
            });
        });
    }

      常规的错误处理,然后判断了下profile属性,这个属性大概是用计算前面factory整个事件流的触发时间。

      总之,接着调用了addModule原型方法

    addModule

    // 第二个参数未传
    addModule(module, cacheGroup) {
        // 这里调用的是原型方法
        /*
            identifier() {
                return this.request;
            }
        */
        // 愚蠢的方法
        const identifier = module.identifier();
        // 空对象没有的
        if (this._modules[identifier]) {
            return false;
        }
        // 缓存名是'm' + request
        const cacheName = (cacheGroup || "m") + identifier;
        // 如果有缓存的情况下
        if (this.cache && this.cache[cacheName]) {
            // ...
        }
        // 这个方法是在父类上面
        // 作用是清空属性
        // 然而并没有什么属性可以清 跳过
        module.unbuild();
        // 将类分别加入各个对象/数组
        this._modules[identifier] = module;
        if (this.cache) {
            this.cache[cacheName] = module;
        }
        this.modules.push(module);
        return true;
    }

      这个方法其实并没有做什么实际的东西,简单来讲只是添加了缓存。

      接下来看下面的代码:

    _addModuleChain(context, dependency, onModule, callback) {
        // ...
    
        this.semaphore.acquire(() => {
            moduleFactory.create({
                contextInfo: {
                    issuer: "",
                    compiler: this.compiler.name
                },
                context: context,
                dependencies: [dependency]
            }, (err, module) => {
                // ...
                // 返回一个true
                const result = this.addModule(module);
                // 跳过下面两步
                if (!result) {
                    // ...
                }
    
                if (result instanceof Module) {
                    // ...
                }
                // 进入这里
                /*
                    (module) => {
                        entry.module = module;
                        this.entries.push(module);
                        module.issuer = null;
                    }
                */
                onModule(module);
    
                this.buildModule(module, false, null, null, (err) => {
                    if (err) {
                        this.semaphore.release();
                        return errorAndCallback(err);
                    }
    
                    if (this.profile) {
                        const afterBuilding = Date.now();
                        module.profile.building = afterBuilding - afterFactory;
                    }
    
                    moduleReady.call(this);
                });
    
                function moduleReady() {
                    this.semaphore.release();
                    this.processModuleDependencies(module, err => {
                        if (err) {
                            return callback(err);
                        }
    
                        return callback(null, module);
                    });
                }
            });
        });
    }

      这个onModule来源于方法的第三个参数,比较简单,没做什么事,然后继续跑调用了原型方法buildModule。

    buildModule

    // this.buildModule(module, false, null, null, (err) => {})
    buildModule(module, optional, origin, dependencies, thisCallback) {
        // 无此事件流
        this.applyPlugins1("build-module", module);
        if (module.building) return module.building.push(thisCallback);
        const building = module.building = [thisCallback];
    
        function callback(err) {
            module.building = undefined;
            building.forEach(cb => cb(err));
        }
        module.build(this.options, this, this.resolvers.normal, this.inputFileSystem, (error) => {
            // ...
        });
    }

      总的来说并没有做什么事,传进来的callback被封装并带入build方法的回调中。

    module.build

    // options为添加了默认参数的配置对象webpack.config.js
    build(options, compilation, resolver, fs, callback) {
        this.buildTimestamp = Date.now();
        this.built = true;
        this._source = null;
        this.error = null;
        this.errors.length = 0;
        this.warnings.length = 0;
        this.meta = {};
    
        return this.doBuild(options, compilation, resolver, fs, (err) => {
            // ...
        });
    }

      一串串的赋值,又是doBuild方法。

    module.doBuild

    doBuild(options, compilation, resolver, fs, callback) {
        this.cacheable = false;
        const loaderContext = this.createLoaderContext(resolver, options, compilation, fs);
    
        runLoaders({
            resource: this.resource,
            loaders: this.loaders,
            context: loaderContext,
            readResource: fs.readFile.bind(fs)
        }, (err, result) => {
            // ...
        });
    }

      呃,已经连续调用好多个函数了。

    createLoaderContext

    createLoaderContext(resolver, options, compilation, fs) {
        const loaderContext = {
            // ..
        };
        compilation.applyPlugins("normal-module-loader", loaderContext, this);
        // 无此值
        if (options.loader)
            Object.assign(loaderContext, options.loader);
    
        return loaderContext;
    }

      这里先不关心这个loaderContext里面到底有什么,因为后面会调用的,直接看事件流'normal-module-loader'。

    // LoaderTargetPlugin.js
    compilation.plugin("normal-module-loader", (loaderContext) => loaderContext.target = this.target);

      这里由于配置对象没有target参数,所以这个没有用。

    // LoaderPlugin.js
    compilation.plugin("normal-module-loader", (loaderContext, module) => {
        loaderContext.loadModule = function loadModule(request, callback) {
            // ...
        };
    });

      这个仅仅是加了个属性方法,跳过。

      最后返回了一个包含很多方法的对象loaderContext。

      第二步是调用runLoaders方法,将之前生成的对象传进去,这里终于要调用loader对文件进行处理了!!!!

      下节可以有干货,这节流水账先这样了。

  • 相关阅读:
    mongoDB使用
    mac环境下mongodb的安装和使用
    statrc部分
    权限部分
    在Linux 安装Python3.5.6详细文档!!!!
    linux回顾
    linux服务配置
    路飞ORM练习
    考试题-路飞中期(卷一)
    git hub命令,上传到github
  • 原文地址:https://www.cnblogs.com/QH-Jimmy/p/8492430.html
Copyright © 2011-2022 走看看