Webpack 是一個模組打包工具。Webpack 可以與不同的 task runner 一起處理依賴包。然而,由於 Webpack 社群開發的外掛越來越全備,使得兩者之間的界限變得模糊了。有時,這些外掛也會用於執行 Webpack 之外的任務,例如清理構建目錄或部署構建。
React,Hot Module Replacement (HMR) 等技術的發展將 Webpack 逐步帶入開發者的視野中,並使得它能夠使用在其他技術棧中,例如 Ruby on Rails。儘管它的名字中帶有一個 Web,但是 Webpack 的應用卻不侷限於 Web 中,其它應用場景 Build Targets 。
如果您想更詳細地瞭解構建工具及其歷史,請檢視。
Webpack 依賴模組
你可以利用 Webpack 對一個包含入口、出口的專案進行打包。其中,入口是打包過程的起始,由你自定義。並且入口本身也是模組,可以匯入其他的模組。
當你使用 Webpack 打包一個專案時,它會遍歷所有匯入的模組,並構建整個面專案對應的依賴關係圖( dependency graph ),然後依據配置生成輸出檔案。此外,還可以定義拆分界限,以在專案內建立單獨的包。
Webpack 本身就支援 ESES2015,CommonJS,以及 AMD,三種檔案格式。這種載入器機制同樣也適用於 CSS,利用 css-loader 對 @import 、url() 兩種語法進行支援。你還可以找到完成特定任務的外掛,例如較小包體積,國際化,HMR等。
依賴圖指定是描述節點如何相互關聯的有向圖。 在這種情況下,圖形定義是通過檔案之間的引用(require,import)關係。 Webpack 在不執行源的情況下靜態遍歷這些源,以生成建立打包所需的圖。
Webpack 的執行過程
Webpack 從圖中 Entry 處開始工作,也就是之前提到的入口。通常 Webpack 會從這幾個 JavaScript 模組開始遍歷。在這個過程中,Webpack 會依據圖中 Loaders 的配置中 test、loader 項來處理某一個模組,例如圖中利用 test: /\.js$/
來匹配對應的 .js 檔案,同時選用 babel-loader 來處理。
解析過程
由於入口也是模組的一部分,當 Webpack 遇到時,就會嘗試依據入口的解析配置進行匹配。除了 node_modules 之外,你還可以利用 Webpack 對特定目錄執行查詢。也可以調整 Webpack 與副檔名匹配的方式,並且設計具體的目錄。具體可以檢視 Consuming Packages。
如果解析失敗,Webpack 會報一個 runtime 的異常。如果 Webpack 想要正確地解析一個檔案,則必須根據載入器的定義對匹配的檔案進行執行處理。每個載入器都是對特定模組內容進行轉換。
一個 Loader 可以通過多種方式來匹配對應檔案,包括檔案型別和檔案系統中的位置。 Webpack 甚至靈活性到允許根據檔案在專案中的匯入位置來進行特定的轉換。
Webpack 的 Loaders 也會執行相同的解析過程。Webpack 允許你應用類似的邏輯來決定應用那個 loader。由於這個原因,相應的載入程式已經解析了自己的配置。 如果Webpack 無法執行載入程式查詢,則會報一個執行時錯誤。
為了解析,webpack 底層依賴了 enhanced-resolve 。
Webpack 解析任何型別的檔案
Webpack 會在構建依賴圖的過程中,解析每一個它遇到的模組。如果入口中包含了一些依賴項,則會針對每個依賴項完成遞迴操作,直到遍歷完成為止。與 Babel 或者 Sass 等這類編譯工具不同的是,Webpack 可以針對任何檔案執行此過程。
Webpack 還允許你控制如何處理遇到的資源。 例如,你可以決定將資源內聯到JavaScript 包以避免請求。 Webpack 還允許你使用 CSS Modules 等技術將樣式與元件結合,從而並避免標準 CSS 樣式問題。 這種靈活性使 Webpack 非常有價值。
儘管 Webpack 主要用於打包 JavaScript,但是它還能用於捕獲影象或者字型等資源,並將生成單獨的檔案。入口只是打包過程的起點,最終的內容完全取決於你的配置方式。
執行過程
假設所有檔案都找到了對應的 Loader,Webpack 則以從下到上,從右到左的方向來執行所匹配到的 Loader(例如``styleLoader(cssLoader('./main.css'))`),並依次通過每個 Loader 執行模組。最終你會得到 Webpack 處理後的包,具體見Loader Definitions。
如果所有的 Loaders 執行過程都在沒有執行時錯誤的情況下完成,則 Webpack 將所以資源打包到一個包中。 外掛(Plugins)允許你在打包過程不同階段攔截執行時事件。
儘管 Loaders 能夠做很多事情,但是它們卻無法勝任一些高階任務。外掛可以攔截 Webpack 提供的打包時的執行事件。例如,MiniCssExtractPlugin 與 Loader 一起使用的時候,可以將 CSS 提取到單獨的檔案中。如果沒有這個外掛,CSS 則將內聯在生成的 JavaScript 檔案中,因為 Webpack 預設將所有程式碼視為 JavaScript。具體見Separating CSS 。
完成
執行完每一個模組之後,Webpack 會輸出最終的包。其中包含著一個引導指令碼,用於描述如何在遊覽器中執行。具體的輸出是依據你的構建目標(不一定非要是 Web)。
以上僅僅是一個簡易的打包過程,你還可以新增更高階的功能。例如,可以定義特殊的分割點,這樣 Webpack 能夠基於應用程式生成單獨包。具體請見Code Splitting。
Webpack 是配置驅動
Webpack 的核心就是所依賴的配置。這裡有一個依據官方 webpack 教程改編的示例配置,其中包含以下要點:
webpack.config.js
const webpack = require("webpack");
module.exports = {
// 打包的入口
entry: {
app: "./entry.js",
},
// 最終輸出的檔案
output: {
// 輸出檔案所在目錄
path: __dirname,
// 輸出名所用模板
filename: "[name].js",
},
// 解析引入的模組
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.js$/,
use: "babel-loader",
exclude: /node_modules/,
},
],
},
// 要執行的額外處理
plugins: [
new webpack.DefinePlugin({ ... }),
],
// 配置 Webpack 如何尋找模組所對應的檔案
resolve: {
alias: { ... },
},
};
複製程式碼
有時候 Webpack 的配置模型有種黑盒的感覺,因為它看起來是龐大而又單一的。在你瞭解其背後思想之前,很難理解 Webpack 究竟做了什麼。
獨一無二的資源
使用 Webpack時,你可以為每個包名稱注入一個雜湊值(例如,app.d587bbd6.js),以便資源更改之後客戶端上的快取的包無效。 Bundle-splitting 使得在理想情況下客戶端僅用重新載入一小部分資料。
模組熱替換
可能你已經熟悉了 LiveReload、BrowserSync 等工具。這些工具會在你更改時自動重新整理遊覽器。而 HMR 更進一步,它允許應用程式在不強制重新整理的情況下更新。雖然這聽起來不那麼特別,但它可以在實踐中產生很大的不同。
在 livereactload、Browserify 中也可以使用HMR,因此它不是 Webpack 獨有的功能。
程式碼分割
除 HMR 外,Webpack 的打包功能也非常強大。 Webpack 允許你以各種方式分割程式碼。 甚至可以在應用程式執行時動態載入程式碼。 這種延遲載入特別適用於規模較大的應用程式,因為可以根據需要動態載入依賴項。
即使是小型應用程式也可以從程式碼分割中受益,因為它允許使用者更快地獲得可用的東西。 畢竟,效能是一項功能。 瞭解這項基本技術是值得的。
結論
Webpack 雖然學習成本比較高。然而,考慮到長期可以節省多少時間和精力,這是一個值得學習的工具。 為了更好地瞭解它與其他工具的比較,請檢視官方比較。
Webpack 並不是一把萬能鑰匙,但是卻能解開打包的鎖。減少開發過程中的顧慮。package.json 和 Webpack 一起使用,會起到 1 + 1 > 2 的效果。
總結:
- Webpack 是一個模組打包工具,但是你也可以使用它來完成一些任務;
- Webpack 底層基於依賴圖,當 Webpack 遍歷資源構建圖,並使用此資訊和配置生成包;
- Webpack 依賴於載入器和外掛。載入器主要是模組級別上執行,而外掛依賴於 Webpack 提供的鉤子,並且可以訪問其執行過程;
- Webpack 的配置描述瞭如何轉換圖形的資源以及它應該生成哪種輸出。 如果使用程式碼分割等功能,則可以將部分資訊包含在本身中;
- 模組熱更換(HMR)有助於推廣 Webpack。 這是一項特性,無需重新整理整頁就可以更新遊覽器中的程式碼來增強開發體驗;
- Webpack 可以生成包含雜湊值的檔名,會使得之前的包無效。
如果你還不能確定是否要使用 Webpack,請看這篇安利軟文Why would I use a Webpack?
PS:翻譯what is webpack。