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

      compilation事件流中,依然只是针对细节步骤做事件流注入,代码流程如图:

    // apply => this-compilation
    // apply => compilation 
    // return compialtion
    const compilation = this.newCompilation(params);
    this.applyPluginsParallel("make", compilation, err => {
        // callback...
    });

      触发完compilation事件流后,会直接返回一个compilation对象,然后触发下一个事件流make。

      make的来源在EntryOptionPlugin插件中,无论entry参数是单入口字符串、单入口数组、多入口对象还是动态函数,都会在引入对应的入口插件后,注入一个make事件。

      这里先以最简单的单入口字符串为例,开始跑make事件流:

    // SingleEntryPlugin
    compiler.plugin("make", (compilation, callback) => {
        // 生成一个类型为single entry的依赖类
        // dep.loc = name
        const dep = SingleEntryPlugin.createDependency(this.entry, this.name);
        compilation.addEntry(this.context, dep, this.name, callback);
    });

    Compilation.addEntry

      这里回到了compilation类中,调用原型函数addEntry。

    class Compilation extends Tapable {
        // ...
        // context => 默认为process.cwd()
        // entry => dep => SingleEntryDependency
        // name => 单入口默认为main
        // callback => 后面的流程
        addEntry(context, entry, name, callback) {
            const slot = {
                name: name,
                module: null
            };
            // 初始为[]
            this.preparedChunks.push(slot);
            this._addModuleChain(context, entry, (module) => { /**/ }, (err, module) => { /**/ });
        }
    }

    Compilation._addModuleChain

      没什么好讲的,直接进入_addModuleChain函数:

    class Compilation extends Tapable {
        // ...
        _addModuleChain(context, dependency, onModule, callback) {
            // profile => options.profile 
            // 不传则start为undefined
            const start = this.profile && Date.now();
            // bail => options.bail
            const errorAndCallback = this.bail ? (err) => {
                callback(err);
            } : (err) => {
                err.dependencies = [dependency];
                this.errors.push(err);
                callback();
            };
    
            if (typeof dependency !== "object" || dependency === null || !dependency.constructor) {
                throw new Error("Parameter 'dependency' must be a Dependency");
            }
            // dependencyFactories包含了所有的依赖集合
            const moduleFactory = this.dependencyFactories.get(dependency.constructor);
            if (!moduleFactory) {
                throw new Error(`No dependency factory available for this dependency type: ${dependency.constructor.name}`);
            }
    
            this.semaphore.acquire(() => { /**/ });
        }
    }

      profile和bail参数大概不会有人传吧,所有直接忽视。

      接下来就是尝试从dependencyFactories中获取依赖类对应的值,这个Map对象所有值的设置都在compilation事件流中,详情可见24节中的流程图,传送门:http://www.cnblogs.com/QH-Jimmy/p/8183840.html

      在这里,依赖类来源于SingleEntryDependency,键值对设置地点同样来源于SingleEntryPlugin:

    // SingleEntryPlugin
    compiler.plugin("compilation", (compilation, params) => {
        const normalModuleFactory = params.normalModuleFactory;
        // 这里
        compilation.dependencyFactories.set(SingleEntryDependency, normalModuleFactory);
    });

      所以很简单,这里调用get后取出来的是normalModuleFactory。

      而这个normalModuleFactory,在18节中有简单介绍并分析了其中RuleSet对module.rules的处理,传送门:http://www.cnblogs.com/QH-Jimmy/p/8109903.html

      

    semaphore

      获取对应的moduleFactory后,调用的this.semaphore其中是生成一个新类:

    this.semaphore = new Semaphore(options.parallelism || 100);

      (类的名字英文翻译是信号机)

      内容比较简单,过一下源码:

    class Semaphore {
        // 一个数字 默认为100
        constructor(available) {
            this.available = available;
            this.waiters = [];
        };
        // 当available大于0时执行callback并减1
        // 否则将callback弹入waiters
        acquire(callback) {
            if (this.available > 0) {
                this.available--;
                callback();
            } else {
                this.waiters.push(callback);
            }
        };
        // 尝试取出最近弹入的callback并在事件流末尾执行
        release() {
            if (this.waiters.length > 0) {
                const callback = this.waiters.pop();
                process.nextTick(callback);
            } else {
                this.available++;
            }
        }
    }

      设计看起来确实像个信号机,构造函数中有一个初始的使用次数以及一个待执行callback数组。

      每次调用acquire时会传入一个callback,如果次数为正就执行callback并将使用次数减1,如果次数用完了,就把这个callback弹入waiters数组中。

      每次调用release时,为尝试取出最新的callback并尽快执行,如果不存在待执行的callback,就将使用次数加1。

    NormalModuleFactory.create

      也就是说,以下代码可以理解成简单的函数调用:

    this.semaphore.acquire(() => {
        moduleFactory.create({
            contextInfo: {
                issuer: "",
                compiler: this.compiler.name
            },
            context: context,
            dependencies: [dependency]
        }, (err, module) => { /* fn... */ });
    });

      这样,流程跑到了normalModuleFactory的原型方法create上。

    class NormalModuleFactory extends Tapable {
        /*
            data => 
            {
                contextInfo:{
                    issuer: '',
                    compiler: this.compiler.name // undefined
                },
                context: context, // process.cwd()
                dependencies: [SingleEntryDependency]
            }
        */
        create(data, callback) {
            const dependencies = data.dependencies;
            // 尝试取缓存
            const cacheEntry = dependencies[0].__NormalModuleFactoryCache;
            if (cacheEntry) return callback(null, cacheEntry);
            // 上下文 => process.cwd()
            const context = data.context || this.context;
            // 入口文件字符串 => ./input.js
            const request = dependencies[0].request;
            const contextInfo = data.contextInfo || {};
            this.applyPluginsAsyncWaterfall("before-resolve", {
                contextInfo,
                context,
                request,
                dependencies
            }, (err, result) => { /**/ });
        }
    }

      这里将上下文、入口文件、入口模块依赖类整合,然后开始触发normalModuleFactory类上的事件流。

      关于normalModuleFactory的事件流注入,全部都在24节中有介绍,再来一个传送门:http://www.cnblogs.com/QH-Jimmy/p/8183840.html

      这里的事件流before-resolve是没有的,所以按照Tapable的中applyPluginsAsyncWaterfall的执行方式:

    Tapable.prototype.applyPluginsAsyncWaterfall = function applyPluginsAsyncWaterfall(name, init, callback) {
        if (!this._plugins[name] || this._plugins[name].length === 0) return callback(null, init);
        // more...
    }

      这里会直接调用callback,分别传入null与第二个参数,如下所示:

    this.applyPluginsAsyncWaterfall("before-resolve", {
            contextInfo,
            context,
            request,
            dependencies
        },
        // err => null
        // result => 上面的对象 
        (err, result) => {
            if (err) return callback(err);
            if (!result) return callback();
            // 触发factory事件流
            const factory = this.applyPluginsWaterfall0("factory", null);
    
            // Ignored
            if (!factory) return callback();
    
            factory(result, (err, module) => { /**/ });
        }
    );

      这里会接着触发factory事件流,这个是在构造函数中直接plugin的。

    class NormalModuleFactory extends Tapable {
        constructor(context, resolvers, options) {
            super();
            // ...other property
            this.plugin("factory", () => (result, callback) => {
                let resolver = this.applyPluginsWaterfall0("resolver", null);
                if (!resolver) return callback();
                resolver(result, (err, data) => { /**/ });
            });
        }
    }

      这里又触发了resolver事件流,同样是构造函数中另外一个plugin的事件。

      这节先到这里。

  • 相关阅读:
    背水一战 Windows 10 (90)
    背水一战 Windows 10 (89)
    背水一战 Windows 10 (88)
    背水一战 Windows 10 (87)
    背水一战 Windows 10 (86)
    背水一战 Windows 10 (85)
    背水一战 Windows 10 (84)
    背水一战 Windows 10 (83)
    背水一战 Windows 10 (82)
    背水一战 Windows 10 (81)
  • 原文地址:https://www.cnblogs.com/QH-Jimmy/p/8250774.html
Copyright © 2011-2022 走看看