What is Webpack

Yzz發表於2019-04-22

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's execution process

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