你想要的——webpack構建過程分析

提及webpack,相信對於前端工程師們而言早已經不是什麼新鮮的事物。可是因爲webpack有着較爲複雜和靈活的配置項,因此給人的第一感受是難以徹底掌握。前端

此次就跟你們分享一下有關webpack構建過程的相關知識,但願對你們進一步理解webpack有所幫助。webpack

本次分析的對象是webpack(v3.6.0),這是gayhub地址 摸我git

因爲webpack的內容實在太多,一會兒講太多東西容易懵逼,咱們此次就從最最最簡單的例子開始講起。github

如下是我寫的一個炒雞簡單的例子:web

// webpack.config.js

var path = require("path");
module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, "build"),
    filename: "bundle.js"
  }
}

假設咱們的工做目錄下有一個這樣的webpack.config.js文件,那麼當咱們執行webpack命令的時候【前提是你已經安裝了webpack】,那麼首先會執行的是 /bin 目錄下的webpack.js前端工程師

OK,做爲入口文件,咱們首先要分析的就是: /bin/webpack.jsapp

這個文件主要作了如下幾件事:函數

  • 引入yargs模塊,對命令行參數進行註釋和重命名等工做
  • 添加處理默認參數(好比當咱們沒有指定--config參數的時候,默認去找當前目錄下的 webpack.config.js文件做爲配置文件)
  • 處理完參數相關的內容以後,就引入lib目錄下的webpack.js文件,獲得編譯對象compiler,而後進行編譯(compiler.run())
var webpack = require("../lib/webpack.js");
...
...
try {
    // 這裏的options就是轉換以後的參數,convert-argv文件主要負責這些工做
    // 獲得compiler對象
    compiler = webpack(options);
}catch(e) {
    ...
    ...
}
...
...
// 執行編譯
compiler.run(compilerCallback)

因爲這裏講的例子比較簡單,不涉及到其餘參數相關的內容,感興趣的同窗能夠進一步瞭解參數處理的部分,這裏就很少作解釋了。重點咱們仍是看一下這個編譯的過程。ui

做爲complier的入口文件,咱們接下來看一下: /lib/webpack.jsthis

  • 這個文件首先調用validateSchema方法對傳入的options進行格式校驗
  • 而後又調用了WebpackOptionsDefaulter實例的process方法對options進一步的處理(保存了兩個實例屬性 default={} , config= {},分別往這兩個對象上面掛屬性,而後再掛載到options上)
  • 而後實例化Compiler類,因爲這個類繼承自Tapable類,因此他具備父類上實現插件的一套機制(applyPlugin,plugin等方法,後面會具體分析這兩個類),獲得compiler對象
  • 而後執行NodeEnvironmentPlugin插件,主要是使用enhanced-resolve模塊修飾compiler對象,註冊「before-run」回調方法
  • 而後調用complier的apply方法,內部其實調用插件的apply方法,至關於註冊各類插件的回調方法
  • 觸發「environment」 和 「after-environment」 回調方法
  • 實例化WebpackOptionsApply類,調用process方法;後面咱們會展開分析這個方法
  • 往webpack這個方法上掛一下靜態屬性(各類插件方法)
  • 導出webpack這個方法

接下來咱們先分析WebpackOptionsApply類:/lib/WebpackOptionsApply.js

從上面看到,在編譯以前,webpack會先實例化WebpackOptionsApply類,而後調用其process方法
咱們看到process方法其實就是註冊了N多個插件,而後觸發了某些插件的回調函數

  • 首先判斷options.target,若是值爲「web」的話(這種狀況是最多見的,其餘狀況的邏輯也是相似的),則註冊插件JsonpTemplatePlugin【註冊「this-compilation」回調】,FunctionModulePlugin【註冊「compilation」回調】,NodeSourcePlugin【註冊「compilation」 & 「after-resolver」回調】,LoaderTargetPlugin【註冊「compilation」回調】。

  • 註冊插件LibraryTemplatePlugin【註冊「compilation」回調】,ExternalsPlugin【註冊「compile」回調】
  • 註冊插件EntryOptionPlugin【註冊「entry-option」回調】
  • 觸發「entry-option」回調,因此進入了EntryOptionPlugin插件的回調函數
  • EntryOptionPlugin類中,經過itemToPlugin方法判斷單入口仍是多入口文件,這裏以單入口爲例,因此進入了SingleEntryPlugin類中,註冊插件SingleEntryPlugin【「compilation」 & 「make」回調】
  • 繼續回到WebpackOptionsApply的process方法,而後又繼續經過compile.apply方法添加插件,插件太多了,不一一列舉,感興趣的能夠跟蹤代碼瞭解詳情
  • 最後觸發「after-plugins」 和 「after-resolvers」 的回調函數

以上就是WebpackOptionsApply實例調用process的全過程

在/bin/webpack中,獲得的是/lib/webpack返回的compiler對象,最後調用compiler對象的run方法。

做爲編譯過程的核心類,咱們接下來看看Compiler這個類:/lib/Compiler.js

咱們看到Compiler類繼承自Tapable類 github地址

Tapable類提供了一種調用插件的方式,webpack所有插件都是基於這種方式來註冊和調用的。

Tapable類定義了plugin方法,用於註冊插件,將插件及其回調函數以key-value的形式保存在內部_plugins={}對象中;
又定義了applyPlugins,applyPluginsWaterfall等方法來觸發插件的回調函數。其實就是一個訂閱-發佈模式的實現。

因此當Compiler類繼承Tapable類後,也一樣具備註冊插件和觸發回調函數的功能。

接下來看看Compiler中的run方法

  • 首先觸發的「before-run」回調函數,NodeEnvironmentPlugin插件註冊了回調函數
  • 而後觸發「run」回調函數,CachePlugin插件註冊了回調函數
  • 調用readRecords方法()
  • 調用compile方法,進入compile過程

    1. 觸發「before-compile」回調函數,DllReferencePlugin註冊了回調函數
    2. 觸發「compile」回調函數,ExternalsPlugin & DllReferencePlugin & DelegatedPlugin註冊了回調函數
    3. 調用newCompilation方法,建立Compilation實例,這個實例包含了編譯過程的全部屬性和方法
    4. 觸發「this-compilation」回調函數
    5. 觸發「compilation」回調函數
    6. 觸發「make」回調函數

      1. 若是是單入口項目,這裏就會觸發SingleEntryPlugin插件註冊的「make」回調,其中調用了compilation的addEntry方法進行模塊構建
      2. 經過compilation的processModuleDependencies方法收集模塊的依賴
      3. 最後經過buildModule方法構建模塊
    7. 調用compilation.finish()方法
    8. 調用compilation.seal()方法
    9. 觸發「after-compile」回調函數
  • compile方法執行完以後,就執行onCompiled回調
  • 觸發「should-emit」回調函數
  • 觸發「done」回調函數
  • 調用emitAssets方法,觸發了「emit」回調函數
  • 調用emitFiles方法,觸發「after-emit」回調函數
  • 最後執行emitRecords方法

這就是compiler的run方法的主要內容分析

以上就是webpack簡單的構建流程的分析。哈哈今天就分享到這裏,但願你們喜歡,祝你們週末愉快啊。。。

相關文章
相關標籤/搜索