關於Webpack詳述系列文章 (第四篇)
1. webpack基本概念
Entry:入口,Webpack 執行構建的第一步將從 Entry 開始,可抽象成輸入。
Module:模塊,在 Webpack 裏一切皆模塊,一個模塊對應著一個文件。Webpack 會從配置的 Entry 開始遞歸找出所有依賴的模塊。
Chunk:代碼塊,一個 Chunk 由多個模塊組合而成,用於代碼合並與分割。
Loader:模塊轉換器,用於把模塊原內容按照需求轉換成新內容。
Plugin:擴展插件,在 Webpack 構建流程中的特定時機會廣播出對應的事件,插件可以監聽這些事件的發生,在特定時機做對應的事情。
2. 流程概括
- 初始化參數:從配置文件和 Shell 語句中讀取與合並參數,得出最終的參數;
- 開始編譯:用上一步得到的參數初始化 Compiler 對象,加載所有配置的插件,執行對象的 run 方法開始執行編譯;
- 確定入口:根據配置中的 entry 找出所有的入口文件;
- 編譯模塊:從入口文件出發,調用所有配置的 Loader 對模塊進行翻譯,再找出該模塊依賴的模塊,再遞歸本步驟直到所有入口依賴的文件都經過了本步驟的處理;
- 完成模塊編譯:在經過第4步使用 Loader 翻譯完所有模塊後,得到了每個模塊被翻譯後的最終內容以及它們之間的依賴關系;
- 輸出資源:根據入口和模塊之間的依賴關系,組裝成一個個包含多個模塊的 Chunk,再把每個 Chunk 轉換成一個單獨的文件加入到輸出列表,這步是可以修改輸出內容的最後機會;
- 輸出完成:在確定好輸出內容後,根據配置確定輸出的路徑和文件名,把文件內容寫入到文件系統。
在以上過程中,Webpack 會在特定的時間點廣播出特定的事件,插件在監聽到感興趣的事件後會執行特定的邏輯,並且插件可以調用 Webpack 提供的 API 改變 Webpack 的運行結果。
3.詳細流程
WebPack的構建流程可以分為以下三大階段:
- 初始化:啟動構建,讀取與合並配置參數,加載 Plugin,實例化 Compiler。
- 編譯:從 Entry 發出,針對每個 Module 串行調用對應的 Loader 去翻譯文件內容,再找到該 Module 依賴的 Module,遞歸地進行編譯處理。
- 輸出:對編譯後的 Module 組合成 Chunk,把 Chunk 轉換成文件,輸出到文件系統。
3.1 初始化階段
事件名 | 解釋 | / |
---|---|---|
初始化參數 | 從配置文件和 Shell 語句中讀取與合並參數,得出最終的參數。 | webpack-cli/bin/webpack.js:436 |
實例化 Compiler | 用上一步得到的參數初始化 Compiler 實例,Compiler 負責文件監聽和啟動編譯。Compiler 實例中包含了完整的 Webpack 配置,全局只有一個 Compiler 實例。 | webpack/lib/webpack.js:32 |
加載插件 | 依次調用插件的 apply 方法,讓插件可以監聽後續的所有事件節點。同時給插件傳入 compiler 實例的引用,以方便插件通過 compiler 調用 Webpack 提供的 API。 | webpack/lib/webpack.js:42 |
environment | 開始應用 Node.js 風格的文件系統到 compiler 對象,以方便後續的文件尋找和讀取。 | webpack/lib/webpack.js:40 |
entry-option | 讀取配置的 Entrys,為每個 Entry 實例化一個對應的 EntryPlugin,為後面該 Entry 的遞歸解析工作做準備。 | webpack/lib/webpack.js:275 |
after-plugins | 調用完所有內置的和配置的插件的 apply 方法。 | webpack/lib/WebpackOptionsApply.js:359 |
after-resolvers | 根據配置初始化完 resolver,resolver 負責在文件系統中尋找指定路徑的文件。 | webpack/lib/WebpackOptionsApply.js:396 |
3.2 編譯階段
事件名 | 解釋 | / |
---|---|---|
run | 啟動一次新的編譯。 | webpack/lib/webpack.js:194 |
watch-run | 和 run 類似,區別在於它是在監聽模式下啟動的編譯,在這個事件中可以獲取到是哪些文件發生了變化導致重新啟動一次新的編譯。 | |
compile | 該事件是為了告訴插件一次新的編譯將要啟動,同時會給插件帶上 compiler 對象。 | webpack/lib/Compiler.js:455 |
compilation | 當 Webpack 以開發模式運行時,每當檢測到文件變化,一次新的 Compilation 將被創建。一個 Compilation 對象包含了當前的模塊資源、編譯生成資源、變化的文件等。Compilation 對象也提供了很多事件回調供插件做擴展。 | webpack/lib/Compiler.js:418 |
make | 一個新的 Compilation 創建完畢,即將從 Entry 開始讀取文件,根據文件類型和配置的 Loader 對文件進行編譯,編譯完後再找出該文件依賴的文件,遞歸的編譯和解析。 | webpack/lib/Compiler.js:459 |
after-compile | 一次 Compilation 執行完成。 | webpack/lib/Compiler.js:467 |
在編譯階段中,最重要的要數 compilation 事件了,因為在 compilation 階段調用了 Loader 完成了每個模塊的轉換操作,在 compilation 階段又包括很多小的事件,它們分別是:
事件名 | 解釋 | / |
---|---|---|
should-emit | 所有需要輸出的文件已經生成好,詢問插件哪些文件需要輸出,哪些不需要。 | webpack/lib/Compiler.js:146 |
emit | 確定好要輸出哪些文件後,執行文件輸出,可以在這裏獲取和修改輸出內容。 | webpack/lib/Compiler.js:287 |
after-emit | 文件輸出完畢。 | webpack/lib/Compiler.js:278 |
done | 成功完成一次完成的編譯和輸出流程。 | webpack/lib/Compiler.js:166 |
failed | 如果在編譯和輸出流程中遇到異常導致 Webpack 退出時,就會直接跳轉到本步驟,插件可以在本事件中獲取到具體的錯誤原因。 |
在輸出階段已經得到了各個模塊經過轉換後的結果和其依賴關系,並且把相關模塊組合在一起形成一個個 Chunk。 在輸出階段會根據 Chunk 的類型,使用對應的模版生成最終要要輸出的文件內容。
4. 代碼流程
4.1 node_modules/.bin/webpack-cli
- 通過yargs獲得shell中的參數
- 把webpack.config.js中的參數和shell參數整合到options對象上
- 調用webpack-cli/bin/webpack.js開始編譯和打包
- webpack-cli/bin/webpack.js中返回一個compiler對象,並調用了compiler.run()
- lib/Compiler.js中,run方法觸發了before-run、run兩個事件,然後通過readRecords讀取文件,通過compile進行打包,該方法中實例化了一個Compilation類
- 打包時觸發before-compile、compile、make等事件
- make事件會觸發SingleEntryPlugin監聽函數,調用compilation.addEntry方法
- 這個方法通過調用其私有方法_addModuleChain完成了兩件事:根據模塊的類型獲取對應的模塊工廠並創建模塊;構建模塊
- 調用loader處理模塊之間的依賴
- 將loader處理後的文件通過acorn抽象成抽象語法樹AST
- 遍歷AST,構建該模塊的所有依賴
- 調用Compilation的finish及seal方法
4.2 關鍵事件
- entry-option:初始化options
- after-plugins
- after-resolvers
- environment
- after-environment
- before-run
- run:開始編譯
- watch-run
- normal-module-factory
- context-module-factory
- before-compile
- compile
- this-compilation
- compilation
- make:從entry開始遞歸分析依賴並對依賴進行build
- build-module:使用loader加載文件並build模塊
- normal-module-loader:對loader加載的文件用acorn編譯,生成抽象語法樹AST
- program:開始對AST進行遍歷,當遇到require時觸發call require事件
- after-compile
- should-emit
- need-additional-pass
- seal:所有依賴build完成,開始對chunk進行優化(抽取公共模塊、加hash等)
- optimize-chunk-assets:優化代碼
- emit:把各個chunk輸出到結果文件
- after-emit
- done
- failed
- invalid
- watch-close
參考
plugins:https://github.com/webpack/docs/wiki/plugins
how to write a plugins:https://github.com/webpack/docs/wiki/how-to-write-a-plugin
Webpack編寫一個插件:https://www.webpackjs.com/contribute/writing-a-plugin/
Webpack編寫一個loader:https://www.webpackjs.com/contribute/writing-a-loader/
關於Webpack詳述系列文章 (第四篇)