细说webpack源码之compile流程-入口函数run


Posted in Javascript onDecember 26, 2017

Webpack是目前基于React和Redux开发的应用的主要打包工具。我想使用Angular 2或其他框架开发的应用也有很多在使用Webpack。

本节流程如图:

细说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事件流,最后调用了回调函数。

总结

以上所述是小编给大家介绍的webpack源码之compile流程-入口函数run,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
不能再简单的无闪刷新验证码原理很简单
Nov 05 Javascript
关于Aptana Studio生成自动备份文件的解决办法
Dec 23 Javascript
jQuery中使用Ajax获取JSON格式数据示例代码
Nov 26 Javascript
jquery用offset()方法获得元素的xy坐标
Sep 06 Javascript
jQuery中detach()方法用法实例
Dec 25 Javascript
JavaScript中判断数据类型的方法总结
May 24 Javascript
归纳下js面向对象的几种常见写法总结
Aug 24 Javascript
AngularJS删除路由中的#符号的方法
Sep 20 Javascript
AngularJS指令与控制器之间的交互功能示例
Dec 14 Javascript
微信小程序实现轮播图效果
Sep 07 Javascript
JS canvas绘制五子棋的棋盘
May 28 Javascript
解决vue使用vant下拉框van-dropdown-item 绑定title值不变问题
Aug 05 Javascript
Vue 进入/离开动画效果
Dec 26 #Javascript
node.js中路由,中间件,ge请求和post请求的参数详解
Dec 26 #Javascript
Angular实现可删除并计算总金额的购物车功能示例
Dec 26 #Javascript
浅谈React深度编程之受控组件与非受控组件
Dec 26 #Javascript
使用vue实现简单键盘的示例(支持移动端和pc端)
Dec 25 #Javascript
vue的一个分页组件的示例代码
Dec 25 #Javascript
jQuery图片查看插件Magnify开发详解
Dec 25 #jQuery
You might like
全国FM电台频率大全 - 2 天津市
2020/03/11 无线电
PHP中常用数组处理方法实例分析
2008/08/30 PHP
PHP反射类ReflectionClass和ReflectionObject的使用方法
2013/11/13 PHP
Fedora下安装php Redis扩展笔记
2014/09/03 PHP
PHP面向对象程序设计之构造方法和析构方法详解
2019/06/13 PHP
laravel unique验证、确认密码confirmed验证以及密码修改验证的方法
2019/10/16 PHP
JQuery实现自定义对话框的代码
2008/06/15 Javascript
javascript将数组插入到另一个数组中的代码
2013/01/10 Javascript
js 获取计算后的样式写法及注意事项
2013/02/25 Javascript
页面加载完后自动执行一个方法的js代码
2014/09/06 Javascript
JavaScript通过字典进行字符串翻译转换的方法
2015/03/19 Javascript
nodejs通过phantomjs实现下载网页
2015/05/04 NodeJs
jquery中添加属性和删除属性
2015/06/03 Javascript
AngularJs Injecting Services Into Controllers详解
2016/09/02 Javascript
EasyUi 打开对话框后控件赋值及赋值后不显示的问题解决办法
2017/01/19 Javascript
springmvc接收jquery提交的数组数据代码分享
2017/10/28 jQuery
Vue实现数据表格合并列rowspan效果
2020/11/30 Javascript
Element的el-tree控件后台数据结构的生成以及方法的抽取
2020/03/05 Javascript
[02:31]2018年度DOTA2最具人气选手-完美盛典
2018/12/16 DOTA
解决python写的windows服务不能启动的问题
2014/04/15 Python
python3使用urllib模块制作网络爬虫
2016/04/08 Python
Python 爬虫之超链接 url中含有中文出错及解决办法
2017/08/03 Python
Python带动态参数功能的sqlite工具类
2018/05/26 Python
Python如何爬取实时变化的WebSocket数据的方法
2019/03/09 Python
彻底理解Python中的yield关键字
2019/04/01 Python
python3 深浅copy对比详解
2019/08/12 Python
python fuzzywuzzy模块模糊字符串匹配详细用法
2019/08/29 Python
Python:slice与indices的用法
2019/11/25 Python
python numpy实现多次循环读取文件 等间隔过滤数据示例
2020/03/14 Python
Python字符串函数strip()原理及用法详解
2020/07/23 Python
python环境搭建和pycharm的安装配置及汉化详细教程(零基础小白版)
2020/08/19 Python
新西兰第一的行李箱网站:luggage.co.nz
2019/07/22 全球购物
副总经理党的群众路线教育实践活动个人对照检查材料思想汇报
2014/10/06 职场文书
考察邀请函范文
2015/01/31 职场文书
大学校园招聘会感想
2015/08/10 职场文书
Nginx+Windows搭建域名访问环境的操作方法
2022/03/17 Servers