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的事件。

      这节先到这里。

  • 相关阅读:
    Week14
    带你一文读懂Batch Normalization
    019_04简易相机
    019_03camera的调用
    019_02视频播放之VideoView
    019_01播放视频之SurfaceView
    018_04音效播放之MediaPlayer与SoundPool
    018_03简易美图
    018_02图片水印
    018_01图片特效
  • 原文地址:https://www.cnblogs.com/QH-Jimmy/p/8250774.html
Copyright © 2011-2022 走看看