闊別兩年,webpack 5 正式釋出了!

語言: CN / TW / HK

HI,繼 Vue 3 和 React 17 之後,我又來啦!印記中文已經完成了 webpack v5 中文文件的同步及翻譯工作,大家可以無縫進行閱讀哦。

文件地址請認準:webpack.docschina.org

文件地址請認準:webpack.docschina.org

文件地址請認準:webpack.docschina.org

重要的事說三遍,我們的文件隸屬於官方,我們沒有其他的域名哦,並且是與官方進行實時同步。

2020 年 10 月 10 日,webpack 升級至 5.0 版本,並且為官網添加了部落格目錄。我們及時的進行了同步,此文是我們閱讀後總結歸納的版本。話不多說開始正文。

自從 2018 年 2 月,webpack4 釋出以來,webpack 就暫時沒有更進一步的重大更新,為了保持 API 的一致性,舊的架構沒有做太多改變,遺留了很多的包袱。闊別 2 年多後,2020 年 10 月 10 日,webpack 5 正式釋出,並帶來了諸多重大的變更,將會使前端工程師的構建效率與質量大為提升。

本次重大發布的整體發展方向如下:

  • 嘗試用永續性快取來提高構建效能。
  • 嘗試用更好的演算法和預設值來改進長期快取。
  • 嘗試用更好的 Tree Shaking 和程式碼生成來改善包大小。
  • 嘗試改善與網路平臺的相容性。
  • 嘗試在不引入任何破壞性變化的情況下,清理那些在實現 v4 功能時處於奇怪狀態的內部結構。
  • 試圖通過現在引入突破性的變化來為未來的功能做準備,儘可能長時間地保持在 v5 版本上。

webpack 5Release Note 非常長,本文嘗試摘出最簡練的資訊。

1. 功能清除

1.1 清理已棄用的功能

所有在 webpack 4 標記即將過期的功能,都已在該版移除。因此在遷移到 webpack 5 之前,請確保你在 webpack 4 執行的構建不會有任何的功能過期警告。

1.2 不再為 Node.js 模組 自動引用 Polyfills

不再為 Node.js 內建模組自動新增 Polyfills。任何專案中有引用 Node.js 內建模組,在 webpack 4 或之前的版本中會自動新增 Polyfills。但 webpack 5 將不會再這樣做,webpack會投入更多的精力到前端模組的相容性工作中。

如果你的程式碼中有引用這些 Node.js 的模組,要升級到 webpack 5, 將盡量使用前端的模組,或者自行手動新增適合的 Polyfills

而針對那些類庫的開發者,請在 package.json 中定義 browser 欄位,使類庫在前端能適用。

2. 針對長期快取的優化

2.1 確定的 Chunk、模組 ID 和匯出名稱

新增了長期快取的演算法。這些演算法在生產模式下是預設啟用的。

chunkIds: "deterministic" moduleIds: "deterministic" mangleExports: "deterministic"

該演算法以確定性的方式為模組和分塊分配短的(3 或 5 位)數字 ID,這是包大小和長期快取之間的一種權衡。由於這些配置將使用確定的 ID 和名稱,這意味著生成的快取失效不再更頻繁。

2.2 真正的內容雜湊

當使用[contenthash]時,Webpack 5 將使用真正的檔案內容雜湊值。之前它 "只 "使用內部結構的雜湊值。當只有註釋被修改或變數被重新命名時,這對長期快取會有積極影響。這些變化在壓縮後是不可見的。

3. 更好的開發支援

3.1 命名程式碼塊 ID

在開發模式下,預設啟用的新命名程式碼塊 ID 演算法為模組(和檔名)提供了人類可讀的名稱。 模組 ID 由其路徑決定,相對於 context。程式碼塊 ID 由程式碼塊的內容決定。

所以你不再需要使用import(/* webpackChunkName: "name" */ "module")來除錯。但如果你想控制生產環境的檔名,還是有意義的。

可以在生產環境中使用 chunkIds: "named" 在生產環境中使用,但要確保不要不小心暴露模組名的敏感資訊。

遷移:如果你不喜歡在開發中改變檔名,你可以通過 chunkIds: "natural" 來使用舊的數字模式。

3.2 模組聯邦

Webpack 5 增加了一個新的功能 "模組聯邦",它允許多個 webpack 構建一起工作。從執行時的角度來看,多個構建的模組將表現得像一個巨大的連線模組圖。從開發者的角度來看,模組可以從指定的遠端構建中匯入,並以最小的限制來使用。

4. 支援嶄新的 Web 平臺功能

對於 Web 平臺的的一些支援 ,Webpack 5 也做了更好的完善更新。

4.1 JSON 模組

比如對 JSON 模組,會與現在的提案保持一致,並且要求進行預設的匯出,否則會有警告資訊。即使使用預設匯出,未使用的屬性也會被 optimization.usedExports 優化丟棄,屬性會被 optimization.mangleExports 優化打亂。

如果想用自定義的 JSON 解析器,可以在 Rule.parser.parse 中指定一個自定義的 JSON 解析器來匯入類似 JSON 的檔案(例如針對 toml、yaml、json5 等)。

4.2 資源模組

Webpack 5 現在已經對錶示資源的模組提供了內建支援。這些模組可以向輸出資料夾傳送一個檔案,或者向 javascript 包注入一個 DataURI。 無論哪種方式,它們都會給出一個 URL 來工作。

它們可以通過多種方式被使用:

  • import url from "./image.png" 和 在module.rule 中設定 type: "asset" 當匹配這樣的匯入時。(老方法)
  • new URL("./image.png", import.meta.url) (新方式)

選擇 "新的方式 "語法是為了允許在沒有打包工具的情況下執行程式碼。這種語法也可以在瀏覽器中的原生 ECMAScript 模組中使用。

4.3 原生 Worker 支援

當把資源的 new URLnew Worker/new SharedWorker/navigator.serviceWorker.register 結合起來時,webpack 會自動為 web worker 建立一個新的入口點(entrypoint)。

new Worker(new URL("./worker.js", import.meta.url))

選擇這種語法也是為了允許在沒有打包工具的情況下執行程式碼。這種語法在瀏覽器的原生 ECMAScript 模組中也可以使用。

4.4 URIs

Webpack 5 支援在請求中處理協議。

  • 支援data:。支援 Base64 或原始編碼。Mimetype 可以在module.rule中被對映到載入器和模組型別。例如:import x from "data:text/javascript,export default 42"
  • 支援file:
  • 支援http(s):,但需要通過new webpack.experiments.s schemesHttp(s)UriPlugin()選擇加入。
    • 預設情況下,當目標為 "web "時,這些 URI 會導致對外部資源的請求(它們是外部資源)。

支援請求中的片段。例如:./file.js#fragment

4.5 非同步模組

Webpack 5 支援所謂的 "非同步模組"。這些模組並不是同步解析的,而是基於非同步和 Promise 的。

通過 "import "匯入它們會被自動處理,不需要額外的語法,而且幾乎看不出區別。

通過require()匯入它們會返回一個解析到匯出的 Promise。

在 webpack 中,有多種方式來擁有非同步模組。

  • 非同步的外部資源(async externals)
  • 新規範中的 WebAssembly 模組
  • 使用頂層 Await 的 ECMAScript 模組。

4.6 外部資源

Webpack 5 增加了更多的外部型別來覆蓋更多的應用:

promise: 一個評估為 Promise 的表示式。外部模組是一個非同步模組,解析值作為模組匯出使用。

import。原生的 import() 用於載入指定的請求,外部模組是一個非同步模組,解析值作為模組匯出。外部模組是一個非同步模組。

module: 尚未實現,但計劃通過 import x from "..." 載入模組。

script: 通過 <script> 標籤載入一個 url,並從一個全域性變數(以及它的可選屬性)中獲取輸出。外部模組是一個非同步模組。

5. 全新的 Node.js 生態特性

現在支援 package.json 中的 exportsimports 欄位。現在起原生支援 Yarn PnP。

更多細節請參見package exports

6. 開發體驗的提升

6.1 經過優化的構建目標(target)

Webpack 5 允許傳遞一個目標列表,並且支援目標的版本。例如 target: "node14"``target: ["web", "es2020"]

這是一個簡單的方法,為 webpack 提供它需要確定的所有資訊:

  • 程式碼塊載入機制,以及
  • 支援的語法,如箭頭函式

6.2 統計

改進了統計測試格式的可讀性和冗餘性。改進了預設值,使其不那麼冗長,也適合大型構建。

6.3 進度

ProgressPlugin外掛也做了一些優化,現在不僅可以統計模組編譯的進度,也可以統計 入口依賴。並且,之前展示進度可能會對構建效能有一定的影響,這次的升級也做了一些效能方面的優化。

6.4 自動新增唯一命名

在 webpack 4 中,多個 webpack 執行時可能會在同一個 HTML 頁面上發生衝突,因為它們使用同一個全域性變數進行程式碼塊載入。為了解決這個問題, 需要為 output.jsonpFunction 配置提供一個自定義的名稱。

Webpack 5 確實會從 package.json name 中自動推斷出一個唯一的構建名稱,並將其作為 output.uniqueName 的預設值。

這個值用於使所有潛在的衝突的全域性變數成為唯一。

遷移: 由於 package.json 中有唯一的名稱,可將 output.jsonpFunction刪除。

6.5 自動新增公共路徑

Webpack 5 會在可能的情況下自動確定 output.publicPath

6.6 Typescript 型別

Webpack 5 從原始碼中生成 typescript 型別,並通過 npm 包暴露它們。

遷移:刪除 @types/webpack。當名稱不同時更新引用。

7. 構建優化

7.1 巢狀的 tree-shaking

webpack 現在能夠跟蹤對匯出的巢狀屬性的訪問。這可以改善重新匯出名稱空間 物件時的 Tree Shaking(清除未使用的匯出和混淆匯出)。

// inner.js
export const a = 1;
export const b = 2;

// module.js
export * as inner from './inner';
// 或 import * as inner from './inner'; export { inner };

// user.js
import * as module from './module';
console.log(module.inner.a);
複製程式碼

在這個例子中,可以在生產模式下刪除匯出的b

7.2 內部模組 tree-shaking

webpack 4 沒有分析模組的匯出和引用之間的依賴關係。webpack 5 有一個新的選項 optimization.innerGraph,在生產模式下是預設啟用的,它可以對模組中的標誌進行分析,找出匯出和引用之間的依賴關係。

在這樣的模組中:

import { something } from './something';

function usingSomething() {
  return something;
}

export function test() {
  return usingSomething();
}
複製程式碼

內部依賴圖演算法會找出 something 只有在使用 test 匯出時才會使用。這允許將更多的出口標記為未使用,並從程式碼包中省略更多的程式碼。

當設定"sideEffects": false時,可以省略更多的模組。在這個例子中,當 test 匯出未被使用時,./something 將被省略。

要獲得未使用的匯出資訊,需要使用 optimization.unusedExports。要刪除無副作用的模組,需要使用optimization.sideEffects。可以分析以下標記:

  • 函式宣告
  • 類宣告
  • 預設匯出export default 或定義變數以下的:
    • 函式表示式
    • 類表示式
    • 順序表示式
    • /*#__PURE__*/ 表示式
    • 區域性變數
    • 引入的捆綁(bindings)

反饋:如果你發現這個分析中缺少什麼,請報告一個問題,我們會考慮增加它。

使用 eval() 將為一個模組放棄這個優化,因為經過 eval 的程式碼可以引用範圍內的任何標記。這種優化也被稱為深度範圍分析。

7.3 CommonJs Tree Shaking

webpack 曾經不進行對 CommonJs 匯出和 require() 呼叫時的匯出使用分析。

webpack 5 增加了對一些 CommonJs 構造的支援,允許消除未使用的 CommonJs 匯出,並從 require() 呼叫中跟蹤引用的匯出名稱。

支援以下構造:

  • exports|this|module.exports.xxx = ...
  • exports|this|module.exports = require("...") (reexport)
  • exports|this|module.exports.xxx = require("...").xxx (reexport)
  • Object.defineProperty(exports|this|module.exports, "xxx", ...)
  • require("abc").xxx
  • require("abc").xxx()
  • 從 ESM 匯入
  • require() 一個 ESM 模組
  • 被標記的匯出型別 (對非嚴格 ESM 匯入做特殊處理):
    • Object.defineProperty(exports|this|module.exports, "__esModule", { value: true|!0 })
    • exports|this|module.exports.__esModule = true|!0
  • 未來計劃支援更多的構造

當檢測到不可分析的程式碼時,webpack 會放棄,並且完全不跟蹤這些模組的匯出資訊(出於效能考慮)。

7.4 開發與生產的一致性問題

我們試圖通過改善兩種模式的相似性,在開發模式的構建效能和避免僅在生產模式的產生的問題之間找到一個很好的平衡點。

Webpack 5 預設在兩種模式下都啟用了 "sideEffects "優化。在 webpack 4 中,由於 package.json 中的"sideEffects"標記不正確,這種優化導致了一些只在生產模式下出現的錯誤。在開發過程中啟用這個優化可以更快更容易地發現這些問題。

在很多情況下,開發和生產都是在不同的作業系統上進行的,檔案系統的大小寫敏感度不同,所以 webpack 5 增加了一些奇怪的大小寫的警告/錯誤。

7.5 改進 target 配置

在 webpack 4 中,"target "是在 "web""node" 之間的一個粗略的選擇(還有一些其他的)。Webpack 5 給你更多的選擇。target選項現在比以前影響了更多關於生成程式碼的事情,比如程式碼塊載入方法、程式碼塊的格式、externals 是否預設被啟用等等。

此外,對於其中的一些情況,在 "web""node" 之間的選擇過於粗略,我們需要更多的資訊。因此,我們允許指定最低版本,例如 "node10.13",並推斷出更多關於目標環境的屬性。

現在也允許用一個數組組合多個目標,webpack 將確定所有目標的最小屬性。使用陣列也很有用,當使用像 "web""node" 這樣沒有提供完整資訊的目標時(沒有版本號)。例如,["web", "es2020"] 結合了這兩個部分目標。

有一個目標 "browserslist",它將使用 browserslist 類庫的資料來確定環境的屬性。當專案中存在可用的 browserslist 配置時,這個目標也會被預設使用。當沒有可用的配置時,預設使用 "web" 目標。

7.6 程式碼塊拆分與模組大小

現在模組的尺寸比單一的數字更好的表達方式。現在有不同型別的大小。

SplitChunksPlugin 現在知道如何處理這些不同的大小,並將它們用於 minSizemaxSize。預設情況下,只有 javascript 大小被處理,但你現在可以傳遞多個值來管理它們:

module.exports = {
  optimization: {
    splitChunks: {
      minSize: {
        javascript: 30000,
        webassembly: 50000,
      },
    },
  },
};
複製程式碼

你仍然可以使用一個數字來表示大小。在這種情況下,webpack 會自動使用預設的大小型別。

mini-css-extract-plugin 使用 css/mini-extra 作為大小型別,並將此大小型別自動新增到預設型別中。

還有其它的一些構建優化,比如單個執行時的改進、模組合併、通用 Tree Shaking 改進、個別生成程式碼的改進、請參閱詳情的 webpack 5 釋出資訊。

8. 效能優化

8.1 持久快取

現在有一個檔案系統快取。它是可選的,可以通過以下配置啟用:

module.exports = {
  cache: {
    // 1. 將快取型別設定為檔案系統
    type: 'filesystem',

    buildDependencies: {
      // 2. 將你的 config 新增為 buildDependency,以便在改變 config 時獲得快取無效
      config: [__filename],

      // 3. 如果你有其他的東西被構建依賴,你可以在這裡新增它們
      // 注意,webpack、載入器和所有從你的配置中引用的模組都會被自動新增
    },
  },
};
複製程式碼

重要說明:

預設情況下,webpack 假定 webpack 所在的 node_modules 目錄只被包管理器修改。對 node_modules 來說,雜湊值和時間戳會被跳過。出於效能考慮,只使用包名和版本。只要不指定resolve.symlinks: false,Symlinks(即npm/yarn link)就沒有問題(無論如何都要避免)。不要直接編輯 node_modules 中的檔案,除非你用 snapshot.managedPaths: []以剔除該優化。當使用 Yarn PnP 時,webpack 假設 yarn 快取是不可改變的(通常是這樣)。你可以使用 snapshot.immutablePaths: [] 來退出這個優化。

快取將預設儲存在 node_modules/.cache/webpack(當使用 node_modules 時)或 .yarn/.cache/webpack(當使用 Yarn PnP 時)中。當所有的外掛都正確處理快取時,你可能永遠都不需要手動刪除它。

許多內部外掛也會使用永續性快取。例如 SourceMapDevToolPlugin (快取 SourceMap 的生成)或 ProgressPlugin (快取模組數量)

永續性快取將根據使用情況自動建立多個快取檔案,以優化對快取的讀寫訪問。

預設情況下,時間戳將用於開發模式的快照,而檔案雜湊將用於生產模式。檔案雜湊也允許在 CI 中使用永續性快取。

8.2 編譯器閒置和關閉

編譯器現在需要在使用後關閉。編譯器現在會進入和離開空閒狀態,並且有這些狀態的鉤子。外掛可能會使用這些鉤子來做不重要的工作。(即將持久快取緩慢地將快取儲存到磁碟上)。在編譯器關閉時--所有剩餘的工作應該儘可能快地完成。一個回撥標誌著關閉完成。

外掛和它們各自的作者應該預料到,有些使用者可能會忘記關閉編譯器。所以,所有的工作最終也應該在空閒狀態下完成。當工作正在進行時,應該防止程序退出。

webpack() 用法在被傳遞迴調時自動呼叫close

遷移:在使用 Node.js API 時,一定要在完成工作後呼叫 Compiler.close

8.3 檔案生成

webpack 過去總是在第一次構建時發出所有的輸出檔案,但在增量(觀察)構建時跳過了寫入未更改的檔案。假設在 webpack 執行時,沒有任何其他東西改變輸出檔案。

增加了永續性快取後,即使在重啟 webpack 程序時,也應該會有類似監聽的體驗,但如果認為即使在 webpack 不執行時也沒有其他東西改變輸出目錄,那這個假設就太強了。

所以 webpack 現在會檢查輸出目錄中現有的檔案,並將其內容與記憶體中的輸出檔案進行比較。只有當檔案被改變時,它才會寫入檔案。 這隻在第一次構建時進行。任何增量構建都會在執行中的 webpack 程序中生成新的資產時寫入檔案。

我們假設 webpack 和外掛只有在內容被改變時才會生成新的資產。應該使用快取來確保在輸入相同時不會生成新的資產。不遵循這個建議會降低效能。

被標記為 [不可變] 的檔案(包括內容雜湊),當已經存在一個同名檔案時,將永遠不會被寫入。我們假設當檔案內容發生變化時,內容雜湊會發生變化。這在一般情況下是正確的,但在 webpack 或外掛開發過程中可能並不總是如此。

9. 重大變更:長期未解決的問題

9.1 單一檔案目標的程式碼分割

只允許啟動單個檔案的目標(如 node、WebWorker、electron main)現在支援執行時自動載入引導所需的依賴程式碼片段。

這允許對這些目標使用 chunks: "all"optimization.runtimeChunk

請注意,如果目標的程式碼塊載入是非同步的,這使得初始評估也是非同步的。當使用 output.library 時,這可能是一個問題,因為現在匯出的值是一個 Promise。

9.2 更新瞭解析器

enhanced-resolve 更新到了 v5,有以下改進:

  • 追蹤更多的依賴關係,比如丟失的檔案。
  • 別名可能有多種選擇
  • 現在可以別名為 false 了。
  • 支援 exportsimports 欄位等功能。
  • 效能提高

9.3 沒有 JS 的程式碼塊

不包含 JS 程式碼的塊,將不再生成 JS 檔案。這就允許有隻包含 CSS 的程式碼塊。

10. 重大變更:未來計劃

10.1 實驗特性

在 webpack 5 中,有一個新的 experiments 配置選項,允許啟用實驗性功能。這使得哪些功能被啟用/使用變得很清楚。

雖然 webpack 遵循語義版本化,但它會對實驗性功能進行例外處理。實驗性功能可能會在 webpack 的次要版本中包含破壞性的變化。 當這種情況發生時,我們會在變更日誌中新增一個明確的註釋。這將使我們能夠更快地迭代實驗性功能,同時也使我們能夠在主要版本上為穩定的功能停留更長時間。

以下的實驗功能將隨 webpack 5 一起釋出。

  • 舊的 WebAssembly 支援,就像 webpack 4 一樣 (experiments.syncWebAssembly)
  • 根據更新的規範(experiments.asyncWebAssembly),新增 WebAssembly 支援。
    • 這使得一個 WebAssembly 模組成為一個非同步模組。
  • 頂層的 Await第三階段提案(experiments.topLevelAwait)
    • 在頂層使用 await 使該模組成為一個非同步模組。
  • 以模組的形式生成程式碼包 (experiments.outputModule)
    • 這就從程式碼包中移除了包裝器 IIFE,執行嚴格模式,通過 <script type="module"> 進行懶惰載入,並在模組模式下最小化壓縮。

請注意,這也意味著 WebAssembly 的支援現在被預設禁用。

10.2 最小 Node.js 版本

最低支援的 Node.js 版本從 6 增加到 10.13.0(LTS)。

遷移:升級到最新的 Node.js 版本。

11. 主要的內部架構變更

這部份內容主要是那些想貢獻 webpack 核心,以及載入器、外掛開發者需要密切關注的。如果你只是使用 webpack,可以忽略這部份。內容非常多,而且比較難懂。

以下咱們來介紹一些最主要的一些內部架構的變更。

11.1 新的外掛執行順序

現在 webpack 5 中的外掛在應用配置預設值之前就會被應用。這使得外掛可以應用自己的預設值,或者作為配置預設。但這也是一個突破性的變化,因為外掛在應用時不能依賴配置值的設定。

遷移:只在外掛鉤子中訪問配置。或者最好完全避免訪問配置,並通過建構函式獲取選項。

11.2 執行時模組

大部分的執行時程式碼被移到了所謂的"執行時模組"中。這些特殊模組負責新增執行時程式碼。它們可以被新增到任何塊中,但目前總是被新增到執行時塊中。 "執行時需求"控制哪些執行時模組(或核心執行時部件)被新增到程式碼包中。這確保了只有使用的執行時程式碼才會被新增到程式碼包中。未來,執行時模組也可以新增到按需載入的塊中,以便在需要時載入執行時程式碼。

在大多數情況下,核心執行程式碼時允許內聯入口模組,而不是用 __webpack_require__ 來呼叫它。如果程式碼包中沒有其他模組,則根本不需要使用__webpack_require__。這與模組合併很好地結合在一起,即多個模組被合併成一個模組。在最好的情況下,根本不需要執行時程式碼。

遷移:如果你在外掛中注入執行時程式碼到 webpack 執行時,可以考慮使用 RuntimeModules 來代替。

11.3 序列化

我們添加了一個序列化機制,以允許在 webpack 中對複雜物件進行序列化。它有一個可選的語義,所以那些應該被序列化的類需要被明確地標記出來(並且實現它們的序列化)。大多數模組、所有的依賴關係和一些錯誤都已經這樣做了。

遷移:當使用自定義模組或依賴關係時,建議將它們實現成可序列化的,以便從持久化快取中獲益。

11.4 用於快取的外掛

增加了一個帶有外掛介面的 Cache 類。該類可用於寫入和讀取快取。根據配置的不同,不同的外掛可以為快取新增功能。MemoryCachePlugin 增加了記憶體快取功能。FileCachePlugin 增加了永續性(檔案系統)快取。FileCachePlugin 使用序列化機制將快取專案持久化到磁碟上或從磁碟上恢復。

11.5 Tapable 外掛升級

webpack 3 外掛的 compat 層已經被移除。它在 webpack 4 中已經被取消了。一些較少使用的 tapable API 被刪除或廢棄。

遷移:使用新的 tapable API。

11.6 Main/Chunk/ModuleTemplate 廢棄

打包模板已經重構。MainTemplate/ChunkTemplate/ModuleTemplate 被廢棄,現在 JavascriptModulesPlugin 負責 JS 模板。

在那次重構之前,JS 輸出由 Main/ChunkTemplate 處理,而另一個輸出(即 WASM、CSS)則由外掛處理。這樣看起來 JS 是一等公民,而其它輸出是二等。重構改變了這一點,所有的輸出都由他們的外掛處理。

依然可以侵入部分模板。鉤子現在在 JavascriptModulesPlugin 中,而不是 Main/ChunkTemplate 中。(是的,外掛也可以有鉤子,我稱之為附加鉤子。)有一個相容層,所以 Main/Chunk/ModuleTemplate 仍然存在,但只是將 tap 呼叫委託給新的鉤子位置。

遷移:按照 deprecation 訊息中的建議。主要是指向不同位置的鉤子。

11.7 入口檔案的新增配置

在 webpack 5 中,入口檔案除了字串、字串陣列,也可以使用描述符進行配置了,如:

module.exports = {
  entry: {
    catalog: {
      import: './catalog.js',
    },
  },
};
複製程式碼

此外,也可以定義輸出的檔名,之前都是通過 output.filename 進行定義的:

module.exports = {
  entry: {
    about: { import: './about.js', filename: 'pages/[name][ext]' },
  },
};
複製程式碼

另外,入口檔案的配置,新增了檔案依賴定義、生成類庫的格式型別(commonjs 或 amd),也可以設定執行時的名字,以及程式碼塊載入的方式,更多細節可以參考完整的釋出記錄。

11.8 排序與 ID

webpack 曾經在編譯階段以特定的方式對模組和程式碼塊進行排序,以遞增的方式分配 ID。現在不再是這樣了。順序將不再用於 ID 的生成,取而代之的是,ID 生成的完全控制在外掛中。優化模組和程式碼塊順序的鉤子已經被移除。

遷移:在編譯階段,你不能再依賴模組和程式碼塊的順序了。

11.9 從陣列到集合(Set)

  • Compilation.modules 現在是一個集合
  • Compilation.chunks 現在是一個集合
  • Chunk.files 現在是一個集合

存在一個適配層但會列印廢棄的警告。

遷移: 使用集合方法代替陣列方法。

11.10 檔案系統與資訊變更

webpack 5 中,一個是需要使用 Compilation.fileSystemInfo 替代 file/contextTimestamps,獲取檔案的時間戳資訊,另一個是新增Compiler.modifiedFiles 以便更容易引用更改後的檔案。

另外,還新增了一個類似於 compiler.inputFileSystemcompiler.outputFileSystem 的新 API compiler.intermediateFileSystem,用於所有不被認為是輸入或輸出的 fs 操作,如寫入 records,快取或輸出 profiling。

11.11 模組熱替換

HMR 執行時已被重構為執行時模組。HotUpdateChunkTemplate 已被合併入 ChunkTemplate 中。ChunkTemplates 和 plugins 也應處理 HotUpdateChunk 了。

HMR 執行時的 javascript 部分已從核心 HMR 執行時鐘分離了出來。其他模組型別現在也可以使用它們自己的方式處理 HMR。在未來,這將使得 HMR 處理諸如 mini-css-extract-plugin 或 WASM 模組。

遷移:此為新功能,無需遷移。

import.meta.webpackHot 公開了與 module.hot 相同的 API。當然可以在 ESM 模組(.mjs,package.json 中的 type: "module")中使用,這些模組不能訪問 module

11.12 工作佇列

webpack 曾經通過函式呼叫函式的形式來進行模組處理,還有一個 semaphore 選項限制並行性。Compilation.semaphore 已被移除,現在可以使用非同步佇列處理,每個步驟都有獨立的佇列:

  • Compilation.factorizeQueue:為一組 dependencies 呼叫模組工廠。
  • Compilation.addModuleQueue:將模組新增到編譯佇列中(可以使用快取恢復模組)
  • Compilation.buildQueue:必要時構建模組(可將模組儲存到快取中)
  • Compilation.rebuildQueue:如需手動觸發,則會重新構建模組
  • Compilation.processDependenciesQueue:處理模組的 dependencies。

這些佇列會有一些 hook 來監聽並攔截工作的程序。未來,多個編譯器會同時工作,可以通過攔截這些佇列來進行編譯工作的編排。

遷移:此為新功能,無需遷移。

11.13 模組和 chunk 圖

webpack 曾經在依賴關係中儲存了已解析的模組,並在 chunk 中儲存引入的模組。但現已發生變化。所有關於模組在模組圖中如何連線的資訊,現在都儲存在 ModulGraph 的 class 中。所有關於模組與 chunk 如何連線的資訊現在都已儲存在 ChunkGraph 的 class 中。依賴於 chunk 圖的資訊也儲存在相關的 class 中。

以下列舉一些模組的資訊已被移動的例子:

  • Module connections -> ModuleGraph
  • Module issuer -> ModuleGraph
  • Module optimization bailout -> ModuleGraph (TODO: check if it should ChunkGraph instead)

當從快取中恢復模組時,webpack 會將模組從圖中斷開。現在已無需這麼做。一個模組不儲存圖形的任何資訊,技術上可以在多個圖形中使用。這會使得快取變得更加容易。這部分變化中大多數都有一個適配層,當使用時,它會列印一個棄用警告。

遷移:在 ModuleGraph 和 ChunkGraph 上使用新的 API。

11.14 模組 Source Types

Modules 現在必須通過 Module.getSourceTypes() 來定義它們支援的原始碼型別。根據這一點,不同的外掛會用這些型別呼叫 source()。對於源型別為 javascriptJavascriptModulesPlugin 會將原始碼嵌入到 bundle 中。源型別 webassemblyWebAssemblyModulesPlugin 會 emit 一個 wasm 檔案。同時,也支援自定義源型別,例如,mini-css-extract-plugin 會使用源型別為 stylesheet 將原始碼嵌入到 css 檔案中。

模組型別與源型別間沒有關係。即使模組型別為 json,也可以使用源型別為 javascript 和模組型別為 webassembly/experimentaljavascriptwebassembly

遷移:自定義模組需要實現這些新的介面方法。

11.15 全新的觀察者

webpack 所使用的觀察者已重構。它之前使用的是 chokidar 和原生依賴 fsevents(僅在 OSX 上)。現在它在只基於原生的 Node.js 中的 fs。這意味著在 webpack 中已經沒有原生依賴了。

它還能在監聽時捕捉更多關於檔案系統的資訊。目前,它還可以捕獲 mtimes 和監視事件時間,以及丟失檔案的資訊。為此,WatchFileSystem API 做了一點小改動。在修改的同時,我們還將 Arrays 轉換為 Sets,Objects 轉換為 Maps。

11.16 SizeOnlySource after emit

webpack 現在使用 SizeOnlySource 替換 Compilation.assets 中的 Sources,以減少記憶體佔用。

11.17 ExportsInfo

重構了模組匯出資訊的儲存方式。ModuleGraph 現在為每個 Module 提供了一個 ExportsInfo,它用於儲存每個 export 的資訊。如果模組僅以副作用的方式使用,它還儲存了關於未知 export 的資訊,

對於每個 export,都會儲存以下資訊:

  • 是否使用 export? 是否使用並不確定。(詳見 optimization.usedExports
  • 是否提供 export? 是否提供並不確定。(詳見 optimization.providedExports
  • 能否重新命名 export 名? 是否重新命名,也不確定
  • 如果 export 已重新命名,則為新名稱。(詳見 optimization.mangleExports
  • 巢狀的 ExportsInfo,如果 export 是一個含有附加資訊的物件,那麼它本身就是一個物件
    • 用於重新匯出名稱空間物件:import * as X from "..."; export { X };
    • 用於表示 JSON 模組中的結構

11.18 程式碼生成階段

編譯的程式碼生成功能作為單獨的編譯階段。它不再隱藏在 Module.source()Module.getRuntimeRequirements() 中運行了。這應該會使得流程更加簡潔。它還執行報告該階段的進度。並使得程式碼生成在剖析時更加清晰可見。

遷移:Module.source()Module.getRuntimeRequirements() 已棄用。使用 Module.codeGeneration() 代替。

11.19 依賴關係參考

webpack 曾經有一個單一的方法和型別來表示依賴關係的引用(Compilation.getDependencyReference 會返回一個 DependencyReference)該型別用於引入關於該引用的所有資訊,如 被引用的模組,已經引入了哪些 export,如果是弱引用,還需要訂閱一些相關資訊。把所有這些資訊構建在一起,拿到參考的成本就很高,而且很頻繁(每次有人需要一個資訊)。

在 webpack5 中,這部分程式碼庫被重構了,方法進行了拆分。

  • 引用的模組可以從 ModuleGraphConnection 中讀取
  • 引入的匯出名,可以通過 Dependency.getReferencedExports() 獲取
  • Dependency 的 class 上會有一個 weak 的 flag
  • 排序只與 HarmonyImportDependencies 相關,可以通過 sourceOrder 屬性獲取

11.20 Presentational Dependencies

這是 NormalModules 的一種新 Dependencies 型別:Presentational Dependencies

這些 dependencies 只在程式碼生成階段使用,但在模組圖構建過程中未使用。所以它們永遠不能引用模組或影響匯出/匯入。

這些依賴關係的處理成本較低,webpack 會盡可能地使用它們

11.21 棄用 loaders

  • null-loader

    已被棄用。使用

    module.exports = {
      resolve: {
        alias: {
          xyz$: false,
        },
      },
    };
    複製程式碼

    或者使用絕對路徑

    module.exports = {
      resolve: {
        alias: {
          [path.resolve(__dirname, '....')]: false,
        },
      },
    };
    複製程式碼

12. 總結

webpack 5 的大部分工作圍繞優化展開,去除了 4 中有廢棄的內容,新增了長期快取,優化了核心等。本文只是挑重點為大家說明,具體變更請大家參考官方文件。

13. 關注我們

我們將為你帶來最前沿的前端資訊。