溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

細說webpack源碼之compile流程-入口函數(shù)run

發(fā)布時間:2020-09-16 17:22:09 來源:腳本之家 閱讀:189 作者:書生小龍 欄目:web開發(fā)

Webpack是目前基于React和Redux開發(fā)的應(yīng)用的主要打包工具。我想使用Angular 2或其他框架開發(fā)的應(yīng)用也有很多在使用Webpack。

本節(jié)流程如圖:

細說webpack源碼之compile流程-入口函數(shù)run

  現(xiàn)在正式進入打包流程,起步方法為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對象?因為構(gòu)造函數(shù)中并沒有一個初始化的方法,只是普通的變量聲明,沒啥好講的。

  在run方法中,首先是調(diào)用了tapable的applyPluginsAsync執(zhí)行了before-run事件流,該事件流的定義地點如下:

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

  在對compiler對象的文件系統(tǒng)方法的掛載插件中,注入了before-run這個事件流,這里首先看一下applyPluginsAsync(做了小幅度的修改以適應(yīng)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函數(shù)]
  args.push(copyProperties(callback, function next(err) {
    // 事件流出錯或者全部執(zhí)行完后調(diào)用回調(diào)函數(shù)
    if (err) return callback(err);
    i++;
    if (i >= plugins.length) {
      return callback();
    }
    // 執(zhí)行下一個事件
    plugins[i].apply(_this, args);
  }));
  // 執(zhí)行第一個事件
  plugins[0].apply(this, args);
};

  當時在第八節(jié)沒有講這個系列的事件流觸發(fā)方式,這里簡單說下:

1、copyProperties用于對象屬性的拷貝,類似于Object.assign,然而在這里傳入的是兩個函數(shù),一點用都沒有!?。。。?當時沒寫講解就是因為一直卡在這個對象拷貝方法在這里有什么毛用)

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

3、注入該事件流的事件必須要執(zhí)行callback方法(如上例),此時執(zhí)行的并不是外部的callback,而是next函數(shù)

4、有兩種情況下會執(zhí)行外部callback,中途出錯或者所有事件流執(zhí)行完畢

  這樣就很明白了,注入before-run中的函數(shù)形參的意義如下:

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

  由于before-run中只有一個事件,所以在調(diào)用內(nèi)部callback的next方法后,會由于i大于事件長度而直接調(diào)用外部callback。 

  這里的purge方法之前見過,這里復習下內(nèi)容:

// 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 { /**/ }
};

  一句話概括就是:清除所有打包中緩存的數(shù)據(jù)。

  由于假設(shè)是第一次,所以這里并沒有什么實際操作,接著調(diào)用外部callback,用同樣的方式觸發(fā)了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 => {
    // ...
  });
});

  在第一次觸發(fā)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中,預覽一下這個函數(shù):

Compiler.prototype.compile = (callback) => {
  const params = this.newCompilationParams();
  // 依次觸發(fā)事件流
  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);
        });
      });
    });
  });
}

  編譯打包的核心流程已經(jīng)一覽無遺,方法中依次觸發(fā)了before-compile、compile、make、after-compile事件流,最后調(diào)用了回調(diào)函數(shù)。

總結(jié)

以上所述是小編給大家介紹的webpack源碼之compile流程-入口函數(shù)run,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對億速云網(wǎng)站的支持!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI