.17-浅析webpack源码之compile流程-入口函数run
本节流程如图:

现在正式进入打包流程,起步方法为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的更多相关文章
- .20-浅析webpack源码之compile流程-Template模块
这里的编译前指的是开始触发主要的事件流this-compilaiton.compilation之前,由于还有一些准备代码,这一节全部弄出来. 模块基本上只走构造函数,具体的方法调用的时候再具体讲解. ...
- .18-浅析webpack源码之compile流程-rules参数处理(1)
Tips:写到这里,需要对当初的规则进行修改.在必要的地方,会在webpack.config.js中设置特殊的参数来跑源码,例如本例会使用module:{rules:[...]}来测试,基本上测试参数 ...
- .19-浅析webpack源码之compile流程-rules参数处理(2)
第一步处理rule为字符串,直接返回一个包装类,很简单看注释就好了. test/include/exclude 然后处理test.include.exclude,如下: if (rule.test | ...
- 51ak带你看MYSQL5.7源码1:main入口函数
从事DBA工作多年 MYSQL源码也是头一次接触 尝试记录下自己看MYSQL5.7源码的历程 目录: 51ak带你看MYSQL5.7源码1:main入口函数 51ak带你看MYSQL5.7源码2:编译 ...
- 从Webpack源码探究打包流程,萌新也能看懂~
简介 上一篇讲述了如何理解tapable这个钩子机制,因为这个是webpack程序的灵魂.虽然钩子机制很灵活,而然却变成了我们读懂webpack道路上的阻碍.每当webpack运行起来的时候,我的心态 ...
- async源码学习 - 控制流程waterfall函数
waterfall函数会连续执行数组中的函数,每次通过数组下一个函数的结果.然而,数组任务中的任意一个函数结果传递失败,那么该函数的下一个函数将不会执行,并且主回调函数立马把错误作为参数执行. 以上是 ...
- .34-浅析webpack源码之事件流make(3)
新年好呀~过个年光打游戏,function都写不顺溜了. 上一节的代码到这里了: // NormalModuleFactory的resolver事件流 this.plugin("resolv ...
- jQuery 2.0.3 源码分析Sizzle引擎 - 编译函数(大篇幅)
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 从Sizzle1.8开始,这是Sizzle的分界线了,引入了编译函数机制 网上基本没有资料细说这个东东的,sizzle引入这 ...
- .29-浅析webpack源码之Resolver.prototype.resolve
在上一节中,最后返回了一个resolver,本质上就是一个Resolver对象: resolver = new Resolver(fileSystem); 这个对象的构造函数非常简单,只是简单的继承了 ...
随机推荐
- 4.update更新和delete删除用法
一.update更新 UserMapper.java package tk.mybatis.simple.mapper; import org.apache.ibatis.annotations.Pa ...
- Ubuntu 修改环境变量
按变量的生存周期来划分,Linux变量可分为两类,它们的修改方法如下:(1)永久的:需要修改配置文件,变量永久生效. 常见的配置文件包括: (1-1)/etc/profile:对所有用户生效:此文件为 ...
- JMeter----正则表达式&JSON Path Extractor
最近在用JMerter给公司一个项目做性能测试,期间遇到要提取上一个接口返回的数据作为下个接口的请求.这里做下记录 如图所示,需要将“扫描二维码”接口请求的返回值中的data部分,作为“处理提交码值” ...
- C# 一些代码小结--UI操作
C# 一些代码小结--UI操作 使用控件名调用控件 object obj = this.GetType().GetField("控件名", System.Reflection.Bi ...
- EF t4模板将实体与DBContext分离
在用EF DBFirst时,实体类是从数据库自动生成的,与DBContext放在同一个项目中.这样其他项目想引用实体,就会将数据库操作类暴露出来.所以,我们需要将实体分离. 新建项目EFAccess, ...
- 背水一战 Windows 10 (40) - 控件(导航类): AppBar, CommandBar
[源码下载] 背水一战 Windows 10 (40) - 控件(导航类): AppBar, CommandBar 作者:webabcd 介绍背水一战 Windows 10 之 控件(导航类) App ...
- MySql字段类型说明
bigint 从 -^ (-) 到 ^- () 的整型数据(所有数字).存储大小为 个字节. P.S. bigint已经有长度了,在mysql建表中的length,只是用于显示的位数 int 从 -^ ...
- 对drf的初步认识
web应用模式 1.前后端不分离 在前后端不分离的应用模式中,前端页面看到的效果都是由后端控制,由后端渲染页面或重定向,也就是后端需要控制前端的展示,前端与后端的耦合度很高. 这种应用模式比较适合纯网 ...
- 将页面中表格数据导出excel格式的文件(vue)
近期由于项目需要,需要将页面中的表格数据导出excel格式的文件,折腾了许久,在网上各种百度,虽然资料不少,但是大都不全,踩了许多坑,总算是皇天不负有心人,最后圆满解决了. 1.安装相关依赖(npm安 ...
- jdk1.8+SpringAOP注解报java.lang.IllegalArgumentException: error at ::0 can't find referenced pointcut select错误的不知原因的解决办法[仅供参考]
先说办法:如果Aspectweaver-1.*.*jar这三个包版本比较低, 比如1.5.0这一层次的,可以找版本高一点的包替换低版本的包,问题可以得到解决 jar包的下载地址:https://mvn ...