webpack編譯流程

中無主而不止,外無正而不行。——莊子javascript

簡介


如今前端開發基本上都會用到reactvue,用到了前端mvcmvvm框架,基本上都會涉及到打包發佈,打包經常使用的工具就是webpackgulp等等。常常使用天然也要了解一些他大體的流程也會方便使用。 首先要理解webpack中比較核心的概念:前端

  • Entry: 指定webpack開始構建的入口模塊,從該模塊開始構建並計算出直接或間接依賴的模塊或者庫。
  • Output:告訴webpack如何命名輸出的文件以及輸出的目錄
  • Module: 模塊,在 Webpack 裏一切皆模塊,一個模塊對應着一個文件。Webpack 會從配置的 Entry 開始遞歸找出全部依賴的模塊。
  • Chunkcoding split的產物,咱們能夠對一些代碼打包成一個單獨的chunk,好比某些公共模塊,去重,更好的利用緩存。或者按需加載某些功能模塊,優化加載時間。在webpack3及之前咱們都利用CommonsChunkPlugin將一些公共代碼分割成一個chunk,實現單獨加載。在webpack4CommonsChunkPlugin被廢棄,使用SplitChunksPlugin
  • Loader:模塊轉換器,用於把模塊原內容按照需求轉換成新內容。
  • Plugin:擴展插件,在 Webpack 構建流程中的特定時機會廣播出對應的事件,插件能夠監聽這些事件的發生,在特定時機作對應的事情。

webpack 執行流程和事件流以下圖所示:vue

webpack complier

webpack編譯過程當中一個比較重要的概念compilercompilation,以下:java

  • Compiler 對象:負責文件監聽和啓動編譯。Compiler 實例中包含了完整的 webpack 配置,全局只有一個 Compiler 實例。
  • Compilation 對象:當 webpack 以開發模式運行時,每當檢測到文件變化,一次新的 Compilation 將被建立。一個 Compilation 對象包含了當前的模塊資源、編譯生成資源、變化的文件等。Compilation 對象也提供了不少事件回調供插件作擴展。

webpack流程


Webpack的運行流程是一個串行的過程,從啓動到結束依次執行如下流程:react

  1. 初始化:啓動構建,讀取與合併配置參數,加載 Plugin,實例化 Compiler。
  2. 編譯:從 Entry 發出,針對每一個 Module 串行調用對應的 Loader 去翻譯文件內容,再找到該 Module 依賴的 Module,遞歸地進行編譯處理。
  3. 輸出:對編譯後的 Module 組合成 Chunk,把 Chunk 轉換成文件,輸出到文件系統。

若是隻執行一次構建,以上階段將會按照順序各執行一次。但在開啓監聽模式下,流程將變爲以下: webpack

webpack-flow

下面具體介紹一下 webpack的三個大階段具體的小步。git

初始化階段

初始化階段大體分爲:github

  • 合併shell配置文件文件的參數而且實例化Complier對象
  • 加載插件
  • 處理入口
事件名 解釋
初始化參數 從配置文件和 Shell 語句中讀取與合併參數,得出最終的參數。 這個過程當中還會執行配置文件中的插件實例化語句 new Plugin()。
實例化 Compiler 用上一步獲得的參數初始化 Compiler 實例,Compiler 負責文件監聽和啓動編譯。Compiler 實例中包含了完整的 Webpack 配置,全局只有一個 Compiler 實例。
加載插件 依次調用插件的 apply 方法,讓插件能夠監聽後續的全部事件節點。同時給插件傳入 compiler 實例的引用,以方便插件經過 compiler 調用 Webpack 提供的 API
environment 開始應用 Node.js 風格的文件系統到 compiler 對象,以方便後續的文件尋找和讀取。
entry-option 讀取配置的 Entrys,爲每一個 Entry 實例化一個對應的 EntryPlugin,爲後面該 Entry 的遞歸解析工做作準備。
after-plugins 調用完全部內置的和配置的插件的 apply 方法。
after-resolvers 根據配置初始化完 resolverresolver 負責在文件系統中尋找指定路徑的文件。

編譯階段

事件名 解釋
before-run 清除緩存
run 啓動一次新的編譯。
watch-run run 相似,區別在於它是在監聽模式下啓動的編譯,在這個事件中能夠獲取到是哪些文件發生了變化致使從新啓動一次新的編譯。
compile 該事件是爲了告訴插件一次新的編譯將要啓動,同時會給插件帶上 compiler 對象。
compilation Webpack 以開發模式運行時,每當檢測到文件變化,一次新的 Compilation 將被建立。一個 Compilation 對象包含了當前的模塊資源、編譯生成資源、變化的文件等。Compilation 對象也提供了不少事件回調供插件作擴展。
make 一個新的 Compilation 建立完畢,即將從 Entry 開始讀取文件,根據文件類型和配置的 Loader 對文件進行編譯,編譯完後再找出該文件依賴的文件,遞歸的編譯和解析。
after-compile 一次 Compilation 執行完成。這裏會根據編譯結果 合併出咱們最終生成的文件名和文件內容。
invalid 當遇到文件不存在、文件編譯錯誤等異常時會觸發該事件,該事件不會致使 Webpack 退出。

這裏主要最重要的就是compilation過程,compilation 實際上就是調用相應的 loader 處理文件生成 chunks並對這些 chunks 作優化的過程。幾個關鍵的事件(Compilation對象this.hooks中):web

事件名 解釋
build-module 使用對應的 Loader 去轉換一個模塊。
normal-module-loader 在用 Loader 對一個模塊轉換完後,使用 acorn 解析轉換後的內容,輸出對應的抽象語法樹(AST),以方便 Webpack 後面對代碼的分析。
program 從配置的入口模塊開始,分析其 AST,當遇到 require 等導入其它模塊語句時,便將其加入到依賴的模塊列表,同時對新找出的依賴模塊遞歸分析,最終搞清全部模塊的依賴關係
seal 全部模塊及其依賴的模塊都經過 Loader 轉換完成後,根據依賴關係開始生成 Chunk

輸出階段

事件名 解釋
should-emit 全部須要輸出的文件已經生成好,詢問插件哪些文件須要輸出,哪些不須要。
emit 肯定好要輸出哪些文件後,執行文件輸出,能夠在這裏獲取和修改輸出內容。
after-emit 文件輸出完畢。
done 成功完成一次完成的編譯和輸出流程。
failed 若是在編譯和輸出流程中遇到異常致使 Webpack 退出時,就會直接跳轉到本步驟,插件能夠在本事件中獲取到具體的錯誤緣由。

Tapable


Webpack能夠將其理解是一種基於事件流的編程範例,一個插件合集。而將這些插件控制在webapck事件流上的運行的就是webpack本身寫的基礎類TapableWebpack事件流機制應用了觀察者模式,和 Node.js 中的 EventEmitter很是類似。 Tapable 有四組成員函數shell

  • plugin(name:string, handler:function):容許將一個自定義插件註冊到 Tapable 實例 的事件中。它的行爲和 EventEmitteron() 方法類似,用來註冊一個處理函數/監聽器,來在信號/事件發生時作一些事情。
  • apply(…pluginInstances: (AnyPlugin|function)[]):AnyPlugin 應該是一個擁有 apply 方法的類(也能夠是一個對象,可是不常見),或者只是一個包含註冊代碼的函數。這個方法只調用插件的定義,從而將真正的事件監聽器能夠註冊到 Tapable 實例的註冊列表中。
  • applyPlugins(name:string, …)*:Tapable 實例能夠經過使用這些函數,在指定的 hash 下應用全部的插件。這一組方法的行爲和 EventEmitteremit() 方法類似,使用多種策略細緻地控制事件的觸發。
  • mixin(pt: Object):一個簡單地方法,使用混入而不是繼承的方式擴展 Tapable 的原型。

上面核心的對象 CompilerCompilation等都是繼承於Tabable類。能夠直接在 CompilerCompilation 對象上廣播和監聽器,方法以下:

/** * 廣播出事件 * event-name 爲事件名稱,注意不要和現有的事件重名 * params 爲附帶的參數 */
  compiler.apply('event-name',params);

  /** * 監聽名稱爲 event-name 的事件,當 event-name 事件發生時,函數就會被執行。 * 同時函數中的 params 參數爲廣播事件時附帶的參數。 */
  compiler.plugin('event-name',function(params) {
    doSomeThing();
  });
複製代碼

同理,compilation.applycompilation.plugin 使用方法和上面一致。

tapable庫暴露了不少Hook(鉤子)類,爲插件提供掛載的鉤子。

const {
    SyncHook,
    SyncBailHook,
    SyncWaterfallHook,
    SyncLoopHook,
    AsyncParallelHook,
    AsyncParallelBailHook,
    AsyncSeriesHook,
    AsyncSeriesBailHook,
    AsyncSeriesWaterfallHook
 } = require("tapable");
複製代碼

以下圖所示tapable上的鉤子:

webpack-flow
tabable的提供了兩類綁定鉤子的方式:

  • AsyncHook(異步鉤子)綁定能夠經過tapAsynctapPromise(以及 tap),執行經過 callAsyncpromise
  • syncHook(鉤子)綁定能夠經過tap執行經過 call

具體的用法請看Tapable

總結

Webpack 的運行流程是一個串行的過程,從啓動到結束會依次執行如下流程:

  1. 初始化參數:從配置文件和 Shell 語句中讀取與合併參數,得出最終的參數
  2. 開始編譯:用上一步獲得的參數初始化 Compiler 對象,加載全部配置的插件,執行對象的 run 方法開始執行編譯
  3. 肯定入口:根據配置中的 entry 找出全部的入口文件
  4. 編譯模塊:從入口文件出發,調用全部配置的 Loader 對模塊進行翻譯,再找出該模塊依賴的模塊,再遞歸本步驟直到全部入口依賴的文件都通過了本步驟的處理
  5. 完成模塊編譯:在通過第4步使用 Loader 翻譯完全部模塊後,獲得了每一個模塊被翻譯後的最終內容以及它們之間的依賴關係
  6. 輸出資源:根據入口和模塊之間的依賴關係,組裝成一個個包含多個模塊的 Chunk,再把每一個 Chunk 轉換成一個單獨的文件加入到輸出列表,這步是能夠修改輸出內容的最後機會
  7. 輸出完成:在肯定好輸出內容後,根據配置肯定輸出的路徑和文件名,把文件內容寫入到文件系統

同時咱們也瞭解了webpack中比較核心的幾個概念compilercompilationtapable

參考

webpack學習筆記(原理,實現loader和插件)

webpack 源碼分析六:webpack 處理流程分析

Webpack原理與實踐(一):打包流程

相關文章
相關標籤/搜索