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

  这节先到这里。

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

  1. .34-浅析webpack源码之事件流make(3)

    新年好呀~过个年光打游戏,function都写不顺溜了. 上一节的代码到这里了: // NormalModuleFactory的resolver事件流 this.plugin("resolv ...

  2. .23-浅析webpack源码之事件流compilation(1)

    正式开始跑编译,依次解析,首先是: compiler.apply( new JsonpTemplatePlugin(options.output), // start new FunctionModu ...

  3. .21-浅析webpack源码之事件流this-compilation

    上一节生成Compilation实例后,添加了一些属性,随后触发this-compilation事件流,如下: Compiler.prototype.newCompilation = (params) ...

  4. .27-浅析webpack源码之事件流make(2)

    上一节跑到了NormalModuleFactory模块,调用了原型方法create后,依次触发了before-rsolve.factory.resolver事件流,这节从resolver事件流开始讲. ...

  5. .24-浅析webpack源码之事件流compilation(2)

    下一个compilation来源于以下代码: compiler.apply(new EntryOptionPlugin()); compiler.applyPluginsBailResult(&quo ...

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

    呃,终于到了这地方-- newCompilation(params) { // ... this.applyPlugins("this-compilation", compilat ...

  7. .25-浅析webpack源码之事件流compilation(3)

    这一节跑下一批plugin. compiler.apply( new EnsureChunkConditionsPlugin(), new RemoveParentModulesPlugin(), n ...

  8. .37-浅析webpack源码之事件流make(4)

    赶紧完结这个系列咯,webpack4都已经出正式版了. 之前的代码搜索到js文件的对应loader,并添加到了对象中返回,流程如下: this.plugin("factory", ...

  9. 浅析libuv源码-node事件轮询解析(3)

    好像博客有观众,那每一篇都画个图吧! 本节简图如下. 上一篇其实啥也没讲,不过node本身就是这么复杂,走流程就要走全套.就像曾经看webpack源码,读了300行代码最后就为了取package.js ...

随机推荐

  1. ELK日志检索并邮件微信通知

    简介 脚本为通过api检索日志内容,并通过邮件或者微信发送出来. 脚本 index检索脚本 #!/usr/bin/env python # coding:utf-8 from elasticsearc ...

  2. qrc资源文件加载后,裸机环境下图片不显示

    问题描述:在qt开发环境下,使用qss进行界面美化工作,里面包含许多图片资源.最后项目决定把这些图片资源和代码一起打包.然后就把图片资源和qss文件一起编入qrc文件中进行编译.在本机开发环境下是没有 ...

  3. sql查询化繁为简 告别rs.getString("XX"),bean属性赋值setXX("XX")

    一.在执行sql语句查询时候,查询的结果是set的map集合(ResultSet): 结果使用rs.getString("XX")获得对应属性的值,赋值到bean对象的相应的属性中 ...

  4. Python学习_12_方法和类定制

    方法 在上一篇随笔中,简单提到了类的某些方法:__init__()等的调用,并简要说明方法和函数的区别. 方法是在类内部定义的函数,方法也是对象,所以方法是类的属性,这就是为什么说实例的方法存在于类定 ...

  5. K:Union-Find(并查集)算法

    相关介绍:  并查集的相关算法,是我见过的,最为之有趣的算法之一.并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题.其相关的实现代码较为简短,实现思想也 ...

  6. Docker三十分钟快速入门(上)

    一.背景 ​ 最近,Docker技术真是一片火热,它的出现也弥补了虚拟机资源消耗过高的问题,直接让虚拟化技术有了质的飞跃.那么本文我们来聊一聊Docker,和大家一起认识Docker,简单入门Dock ...

  7. python3之内置函数

    1.abs() 取数字的绝对值 >>> print(abs(-28)) 28 >>> print(abs(-2.34)) 2.34 >>> pri ...

  8. Django--进阶--中间件的使用

    - 中间件是什么? 中间件是一个钩子框架,它们可以介入Django 的请求和响应处理过程.它是一个轻量级.底层的“插件”系统,用于在全局修改Django 的输入或输出. 每个中间件组件负责完成某个特定 ...

  9. [转]python变量作用域的有趣差别

    func()里 可以访问全局变量i,但不能给i重新赋值. i = 1 def func(): print( i + 1) func() # 2 用global声明后,可以给i重新赋值. i = 1 d ...

  10. Linux中oops信息调试【转】

    1.Oops 信息来源及格式 Oops 这个单词含义为“惊讶”,当内核出错时(比如访问非法地址)打印出来的信息被称为 Oops 信息. 2.Oops 信息包含以下几部分内容 2.1 一段文本描述信息. ...