细说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 相关文章推荐
向大师们学习Javascript(视频与PPT)
Dec 27 Javascript
JavaScript ECMA-262-3 深入解析.第三章.this
Sep 28 Javascript
node.js中的fs.renameSync方法使用说明
Dec 16 Javascript
jquery获取及设置outerhtml的方法
Mar 09 Javascript
基于gulp合并压缩Seajs模块的方式说明
Jun 14 Javascript
原生js实现简单的Ripple按钮实例代码
Mar 24 Javascript
微信小程序使用picker实现时间和日期选择框功能【附源码下载】
Dec 11 Javascript
JavaScript实现JSON合并操作示例【递归深度合并】
Sep 07 Javascript
ES6的Fetch异步请求的实现方法
Dec 07 Javascript
vue element-ui table组件动态生成表头和数据并修改单元格格式 父子组件通信
Aug 15 Javascript
浅谈Vue使用Elementui修改默认的最快方法
Dec 05 Vue.js
JS中如何优雅的使用async await详解
Oct 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
php下判断数组中是否存在相同的值array_unique
2008/03/25 PHP
某大型网络公司应聘时的笔试题目附答案
2008/03/27 PHP
PHP中的integer类型使用分析
2010/07/27 PHP
PHP学习笔记之二
2011/01/17 PHP
php简单的会话类代码
2011/08/08 PHP
php+MySQL判断update语句是否执行成功的方法
2014/08/28 PHP
php 魔术方法详解
2014/11/11 PHP
如何在HTML 中嵌入 PHP 代码
2015/05/13 PHP
PHP精确计算功能示例
2016/11/29 PHP
游戏人文件夹程序 ver 4.03
2006/07/14 Javascript
iframe的onload在Chrome/Opera中执行两次Bug的解决方法
2011/03/17 Javascript
js 验证密码强弱的小例子
2013/03/21 Javascript
利用JQuery制作符合Web标准的QQ弹出消息
2014/01/14 Javascript
Javascript玩转继承(一)
2014/05/08 Javascript
教你如何在 Javascript 文件里使用 .Net MVC Razor 语法
2014/07/23 Javascript
js实现3D图片展示效果
2017/03/09 Javascript
JS实现向iframe中表单传值的方法
2017/03/24 Javascript
bootstrap fileinput组件整合Springmvc上传图片到本地磁盘
2017/05/11 Javascript
Node.js 8 中的重要新特性
2017/06/28 Javascript
vue自定义一个v-model的实现代码
2018/06/21 Javascript
Vue实现点击按钮复制文本内容的例子
2019/11/09 Javascript
vue实现简易的双向数据绑定
2020/12/29 Vue.js
一起深入理解js中的事件对象
2021/02/06 Javascript
[02:31]2018年度DOTA2最具人气选手-完美盛典
2018/12/16 DOTA
Python批量重命名同一文件夹下文件的方法
2015/05/25 Python
python获取当前运行函数名称的方法实例代码
2017/04/06 Python
python+selenium实现京东自动登录及秒杀功能
2017/11/18 Python
使用anaconda的pip安装第三方python包的操作步骤
2018/06/11 Python
python如何发送带有附件、正文为HTML的邮件
2021/02/27 Python
CSS3媒体查询(Media Queries)介绍
2013/09/12 HTML / CSS
H5最强接口之canvas实现动态图形功能
2019/05/31 HTML / CSS
Omio西班牙:全欧洲低价大巴、火车和航班搜索和比价
2017/02/11 全球购物
工业设计专业个人求职信范文
2013/12/28 职场文书
自我鉴定写作要点
2014/01/17 职场文书
先进党支部事迹材料2016
2016/02/26 职场文书
用几道面试题来看JavaScript执行机制
2021/04/30 Javascript