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

  这节先到这里。

原文地址:https://www.cnblogs.com/QH-Jimmy/p/8250774.html

时间: 2024-08-30 03:16:34

.26-浅析webpack源码之事件流make(1)的相关文章

浅析libuv源码-node事件轮询解析(1)

好久没写东西了,过了一段咸鱼生活,无意中想起了脉脉上面一句话: 始终保持自己的竞争力.所以,继续开写! 一般的JavaScript源码看的已经没啥意思了,我也不会写什么xx入门新手教程,最终决定还是啃原来的硬骨头,从外层libuv => node => v8一步步实现原有的目标吧. libuv核心还是事件轮询,前几天从头到尾看了一遍官网的文档,对此有了一些更深的理解. (虽然现在开发用的mac,但是为了衔接前面的文章,所以代码仍旧以windows系统为基础,反正差别也不大) 首先看一眼官网给的

浅析libuv源码-node事件轮询解析(4)

这篇应该能结,简图如下. 上一篇讲到了uv__work_submit方法,接着写了. void uv__work_submit(uv_loop_t* loop, struct uv__work* w, enum uv__work_kind kind, void (*work)(struct uv__work* w), void (*done)(struct uv__work* w, int status)) { // 上篇主要讲的这里 初始化线程池等 uv_once(&once, init_on

webpack源码分析——配置调试环境

无论是阅读webpack源码,还是编写webpack的plugin和loader,配置调试环境都是很有必要的.weabpack的运行环境是nodejs,调试webpack就是调试nodejs程序.我们平时使用的IDE如eclipse.webstorm都支持nodejs的调试.本文以eclipse(Version: Oxygen.1a Release (4.7.1a))为例,进行讲解. 在这个例子里面,我们使用webpack <entry> [<entry>] <output&

Spark2.1.0之源码分析——事件总线

阅读提示:阅读本文前,最好先阅读<Spark2.1.0之源码分析--事件总线>.<Spark2.1.0事件总线分析--ListenerBus的继承体系>及<Spark2.1.0事件总线分析--SparkListenerBus详解>几篇文章的内容. LiveListenerBus继承了SparkListenerBus,并实现了将事件异步投递给监听器,达到实时刷新UI界面数据的效果.LiveListenerBus主要由以下部分组成: eventQueue:是SparkLis

View源码-Touch事件

在Android-27中查看源码: 首先我们来查看单个View的触摸事件的处理,在View的dispatchTouchEvent方法中看看源码是如何处理的. public boolean dispatchTouchEvent(MotionEvent event) { // If the event should be handled by accessibility focus first. if (event.isTargetAccessibilityFocus()) { // We don'

webpack源码-依赖收集

webpack源码-依赖收集 version:3.12.0 程序主要流程: 触发make钩子 Compilation.js 执行EntryOptionPlugin 中注册的make钩子 执行compilation.addEntry 执行compilation._addModuleChain Compilation.js 执行moduleFactory.create(this.semaphore.avaiable 初始化为100) Compilation.js 执行this.buildModule

.22-浅析webpack源码之compile流程-事件流compilation总览

呃,终于到了这地方-- newCompilation(params) { // ... this.applyPlugins("this-compilation", compilation, params); // 31 console.log(this._plugins['compilation'].length); this.applyPlugins("compilation", compilation, params); return compilation;

jQuery 2.0.3 源码分析 事件绑定 - bind/live/delegate/on

转:http://www.cnblogs.com/aaronjs/p/3440647.html?winzoom=1 事件(Event)是JavaScript应用跳动的心脏,通过使用JavaScript ,你可以监听特定事件的发生,并规定让某些事件发生以对这些事件做出响应 事件的基础就不重复讲解了,本来是定位源码分析实现的, 所以需要有一定的基础才行 为了下一步更好的理解内部的实现,所以首先得清楚的认识到事件接口的划分 网上资料遍地都是,但是作为一个jQuery系列的源码分析,我还是很有必要在重新

浅析Scanf源码

记得当初从C语言学习开始就使用scanf,关于scanf的用法也略知一二,对使用scanf出现的问题并未进行深刻探究,故笔者打算对scanf实现进行探究. 如何找到scanf源码 关于VC中的CRT代码在 VS目录下的\VC\crt\src中,我们就先把scanf.c扒出来. int __cdecl scanf ( const char *format, ... ) { va_list arglist; va_start(arglist, format); return vscanf_fn(_i