關於Webpack詳述系列文章 (第四篇)

1. webpack基本概念

Entry:入口,Webpack 執行構建的第一步將從 Entry 開始,可抽象成輸入。
Module:模塊,在 Webpack 裏一切皆模塊,一個模塊對應着一個文件。Webpack 會從配置的 Entry 開始遞歸找出全部依賴的模塊。
Chunk:代碼塊,一個 Chunk 由多個模塊組合而成,用於代碼合併與分割。
Loader:模塊轉換器,用於把模塊原內容按照需求轉換成新內容。
Plugin:擴展插件,在 Webpack 構建流程中的特定時機會廣播出對應的事件,插件能夠監聽這些事件的發生,在特定時機作對應的事情。node

2. 流程歸納

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

    在以上過程當中,Webpack 會在特定的時間點廣播出特定的事件,插件在監聽到感興趣的事件後會執行特定的邏輯,而且插件能夠調用 Webpack 提供的 API 改變 Webpack 的運行結果。webpack

3.詳細流程

WebPack的構建流程能夠分爲如下三大階段:git

  • 初始化:啓動構建,讀取與合併配置參數,加載 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 階段又包括不少小的事件,它們分別是:github

事件名 解釋 /
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 的類型,使用對應的模版生成最終要要輸出的文件內容。web

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/shell

相關文章
相關標籤/搜索