本节流程如图:

  现在正式进入打包流程,起步方法为run:

Compiler.prototype.run = (callback) => {
const startTime = Date.now(); const onCompiled = (err, compilation) => { /**/ }; this.applyPluginsAsync("before-run", this, err => {
if (err) return callback(err); this.applyPluginsAsync("run", this, err => {
if (err) return callback(err); this.readRecords(err => {
if (err) return callback(err); this.compile(onCompiled);
});
});
});
}

  为什么不介绍compiler对象?因为构造函数中并没有一个初始化的方法,只是普通的变量声明,没啥好讲的。

  在run方法中,首先是调用了tapable的applyPluginsAsync执行了before-run事件流,该事件流的定义地点如下:

// NodeEnvironmentPlugin
compiler.plugin("before-run", (compiler, callback) => {
if (compiler.inputFileSystem === inputFileSystem)
inputFileSystem.purge();
callback();
});

  在对compiler对象的文件系统方法的挂载插件中,注入了before-run这个事件流,这里首先看一下applyPluginsAsync(做了小幅度的修改以适应webpack源码):

// tapable
Tapable.prototype.applyPluginsAsync = (name, ...args, callback) => {
var plugins = this._plugins[name];
if (!plugins || plugins.length === 0) return callback();
var i = 0;
var _this = this;
// args为[args,next函数]
args.push(copyProperties(callback, function next(err) {
// 事件流出错或者全部执行完后调用回调函数
if (err) return callback(err);
i++;
if (i >= plugins.length) {
return callback();
}
// 执行下一个事件
plugins[i].apply(_this, args);
}));
// 执行第一个事件
plugins[0].apply(this, args);
};

  当时在第八节没有讲这个系列的事件流触发方式,这里简单说下:

1、copyProperties用于对象属性的拷贝,类似于Object.assign,然而在这里传入的是两个函数,一点用都没有!!!!!(当时没写讲解就是因为一直卡在这个对象拷贝方法在这里有什么毛用)

2、在webpack中,args为一个this,指向compiler的上下文

3、注入该事件流的事件必须要执行callback方法(如上例),此时执行的并不是外部的callback,而是next函数

4、有两种情况下会执行外部callback,中途出错或者所有事件流执行完毕

  这样就很明白了,注入before-run中的函数形参的意义如下:

// before-run
// compiler => this
// callback => next
(compiler, callback) => {
if (compiler.inputFileSystem === inputFileSystem)
inputFileSystem.purge();
callback();
}

  由于before-run中只有一个事件,所以在调用内部callback的next方法后,会由于i大于事件长度而直接调用外部callback。

  这里的purge方法之前见过,这里复习下内容:

// NodeEnvironmentPlugin
compiler.inputFileSystem = new CachedInputFileSystem(new NodeJsInputFileSystem(), 60000); // CachedInputFileSystem
CachedInputFileSystem.prototype.purge = function(what) {
this._statStorage.purge(what);
this._readdirStorage.purge(what);
this._readFileStorage.purge(what);
this._readlinkStorage.purge(what);
this._readJsonStorage.purge(what);
}; // CachedInputFileSystem => Storage
Storage.prototype.purge = function(what) {
if (!what) {
this.count = 0;
clearInterval(this.interval);
this.nextTick = null;
this.data.clear();
this.levels.forEach(function(level) {
level.clear();
});
} else if (typeof what === "string") { /**/ } else { /**/ }
};

  一句话概括就是:清除所有打包中缓存的数据。

  由于假设是第一次,所以这里并没有什么实际操作,接着调用外部callback,用同样的方式触发了run事件流。

  run事件流也只有一个方法,来源于CachePlugin插件:

Compiler.plugin("run", (compiler, callback) => {
// 这个属性我暂时也不知道是啥 反正直接callback了
if (!compiler._lastCompilationFileDependencies) return callback();
const fs = compiler.inputFileSystem;
const fileTs = compiler.fileTimestamps = {};
asyncLib.forEach(compiler._lastCompilationFileDependencies, (file, callback) => {
// ...
}, err => {
// ...
});
});

  在第一次触发run事件流时,那个属性是undefined,所以会直接跳过,因为我是边看源码边解析,所以也不知道是啥,哈哈。

  

  接下来下一个callback是这个:

this.readRecords(err => {
if (err) return callback(err);
this.compile(onCompiled);
});

  这是另一个原型方法,源码如下:

Compiler.prototype.readRecords = (callback) => {
// 这个属性也没有
if (!this.recordsInputPath) {
this.records = {};
return callback();
}
this.inputFileSystem.stat(this.recordsInputPath, err => {
// ...
});
}

  这里第一次也会跳过并直接callback,看源码大概是传入一个路径并读取里面的文件信息缓存到records中。

  这下连跳两步,直接进入原型方法compile中,预览一下这个函数:

Compiler.prototype.compile = (callback) => {
const params = this.newCompilationParams();
// 依次触发事件流
this.applyPluginsAsync("before-compile", params, err => {
if (err) return callback(err);
this.applyPlugins("compile", params);
const compilation = this.newCompilation(params);
this.applyPluginsParallel("make", compilation, err => {
if (err) return callback(err);
compilation.finish();
compilation.seal(err => {
if (err) return callback(err);
this.applyPluginsAsync("after-compile", compilation, err => {
if (err) return callback(err);
return callback(null, compilation);
});
});
});
});
}

  编译打包的核心流程已经一览无遗,方法中依次触发了before-compile、compile、make、after-compile事件流,最后调用了回调函数。

  从下一节开始详细讲解每一步的流程(不懂的地方肯定会跳过啦)。

.17-浅析webpack源码之compile流程-入口函数run的更多相关文章

  1. .20-浅析webpack源码之compile流程-Template模块

    这里的编译前指的是开始触发主要的事件流this-compilaiton.compilation之前,由于还有一些准备代码,这一节全部弄出来. 模块基本上只走构造函数,具体的方法调用的时候再具体讲解. ...

  2. .18-浅析webpack源码之compile流程-rules参数处理(1)

    Tips:写到这里,需要对当初的规则进行修改.在必要的地方,会在webpack.config.js中设置特殊的参数来跑源码,例如本例会使用module:{rules:[...]}来测试,基本上测试参数 ...

  3. .19-浅析webpack源码之compile流程-rules参数处理(2)

    第一步处理rule为字符串,直接返回一个包装类,很简单看注释就好了. test/include/exclude 然后处理test.include.exclude,如下: if (rule.test | ...

  4. 51ak带你看MYSQL5.7源码1:main入口函数

    从事DBA工作多年 MYSQL源码也是头一次接触 尝试记录下自己看MYSQL5.7源码的历程 目录: 51ak带你看MYSQL5.7源码1:main入口函数 51ak带你看MYSQL5.7源码2:编译 ...

  5. 从Webpack源码探究打包流程,萌新也能看懂~

    简介 上一篇讲述了如何理解tapable这个钩子机制,因为这个是webpack程序的灵魂.虽然钩子机制很灵活,而然却变成了我们读懂webpack道路上的阻碍.每当webpack运行起来的时候,我的心态 ...

  6. async源码学习 - 控制流程waterfall函数

    waterfall函数会连续执行数组中的函数,每次通过数组下一个函数的结果.然而,数组任务中的任意一个函数结果传递失败,那么该函数的下一个函数将不会执行,并且主回调函数立马把错误作为参数执行. 以上是 ...

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

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

  8. jQuery 2.0.3 源码分析Sizzle引擎 - 编译函数(大篇幅)

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 从Sizzle1.8开始,这是Sizzle的分界线了,引入了编译函数机制 网上基本没有资料细说这个东东的,sizzle引入这 ...

  9. .29-浅析webpack源码之Resolver.prototype.resolve

    在上一节中,最后返回了一个resolver,本质上就是一个Resolver对象: resolver = new Resolver(fileSystem); 这个对象的构造函数非常简单,只是简单的继承了 ...

随机推荐

  1. PhantomJS快速入门-无界面浏览器

    https://blog.csdn.net/libsyc/article/details/78199850 PhantomJS快速入门 本文简要介绍了PhantomJS的相关基础知识点,主要包括Pha ...

  2. 中科大debian 9 + docker源设置

    wget https://mirrors.ustc.edu.cn/repogen/conf/debian-http-4-stretch -O sources.list sudo apt-get ins ...

  3. node.js global object,util and so on

    核心模块主要内容: 全局对象 常用工具 事件机制 文件系统访问 http服务器和客户端 global object: 所有的全局变量(除了global本身外)都是global object 的属性(a ...

  4. HttpWebRequest 跳转后(301,302)ResponseUri乱码问题

    问题: 目标地址: http://www.baidu.com/baidu.php?url=a000000aa.7D_ifdr1XkSUzuBz3rd2ccvp2mFoJ3rOUsnx8OdxeOeOL ...

  5. BZOJ 2200--[Usaco2011 Jan]道路和航线(最短路&拓扑排序)

    2200: [Usaco2011 Jan]道路和航线 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 1128  Solved: 414[Submit] ...

  6. USACO December 铂金Maxflow

    USACO 2015 December Contest, Platinum Problem 1. Max Flow Farmer John has installed a new system of ...

  7. skynet inject address file.lua

    inject d test/inject_fuck.lua -- d 是服务的 handle 拿 simpledb.lua 举例,修改如下 local skynet = require "s ...

  8. 解压cpio.gz

    #gunzip 文件名.cpio.gz #cpio -idmv < 文件名.cpio

  9. css3中那些鲜为人知但又很有用的属性

    概述 这是我在写移动端页面的时候遇到的,css3中鲜为人知但又很有用的属性,记录下来,供以后开发时参考,相信对其他人也有用. tap-highlight-color 在移动端开发中,我们需要在用户轻按 ...

  10. Redhat/CentOS 制作本地yum源

    一.制作本地yum源的场景有: (1) 操作系统ISO文件是通过光驱读取的 (2) 操作系统ISO文件是通过USB设备挂载的 (3) 操作系统ISO文件是被上传到本地文件夹的形式 二. 这3种配置方式 ...