App下載

闊別兩年,webpack 5 正式發(fā)布了!

猿友 2020-10-12 14:24:39 瀏覽數 (2538)
反饋

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

文檔地址請認準:https://webpack.docschina.org

文檔地址請認準:https://webpack.docschina.org

文檔地址請認準:https://webpack.docschina.org

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

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

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

本次重大發(fā)布的整體發(fā)展方向如下:

  • 嘗試用持久性緩存來提高構建性能。

  • 嘗試用更好的算法和默認值來改進長期緩存。

  • 嘗試用更好的 Tree Shaking 和代碼生成來改善包大小。

  • 嘗試改善與網絡平臺的兼容性。

  • 嘗試在不引入任何破壞性變化的情況下,

    • 清理那些在實現(xiàn) v4 功能時處于奇怪狀態(tài)的內部結構。

  • 試圖通過現(xiàn)在引入突破性的變化來為未來的功能做準備,

    • 盡可能長時間地保持在 v5 版本上。

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

功能清除

清理已棄用的功能

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

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

不再為 Node.js 內置模塊自動添加 Polyfills。任何項目中有引用 Node.js 內置模塊,在 webpack 4 或之前的版本中會自動添加 Polyfills。但 webpack 5 將不會再這樣做,webpack會投入更多的精力到前端模塊的兼容性工作中。

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

而針對那些類庫的開發(fā)者,請在 package.json 中定義 browser 字段,使類庫在前端能適用。

針對長期緩存的優(yōu)化

確定的 Chunk、模塊 ID 和導出名稱

新增了長期緩存的算法。這些算法在生產模式下是默認啟用的。

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

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

真正的內容哈希

當使用[contenthash]時,Webpack 5 將使用真正的文件內容哈希值。之前它 "只 "使用內部結構的哈希值。當只有注釋被修改或變量被重命名時,這對長期緩存會有積極影響。這些變化在壓縮后是不可見的。

更好的開發(fā)支持

命名代碼塊 ID

在開發(fā)模式下,默認啟用的新命名代碼塊 ID 算法為模塊(和文件名)提供了人類可讀的名稱。模塊 ID 由其路徑決定,相對于 context。代碼塊 ID 由代碼塊的內容決定。

所以你不再需要使用import(/* webpackChunkName: "name" */ "module")來調試。但如果你想控制生產環(huán)境的文件名,還是有意義的。

可以在生產環(huán)境中使用 chunkIds: "named" 在生產環(huán)境中使用,但要確保不要不小心暴露模塊名的敏感信息。

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

模塊聯(lián)邦

Webpack 5 增加了一個新的功能 "模塊聯(lián)邦",它允許多個 webpack 構建一起工作。從運行時的角度來看,多個構建的模塊將表現(xiàn)得像一個巨大的連接模塊圖。從開發(fā)者的角度來看,模塊可以從指定的遠程構建中導入,并以最小的限制來使用。

支持嶄新的 Web 平臺功能

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

JSON 模塊

比如對 JSON 模塊,會與現(xiàn)在的提案保持一致,并且要求進行默認的導出,否則會有警告信息。即使使用默認導出,未使用的屬性也會被 optimization.usedExports 優(yōu)化丟棄,屬性會被 optimization.mangleExports 優(yōu)化打亂。

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

資源模塊

Webpack 5 現(xiàn)在已經對表示資源的模塊提供了內置支持。這些模塊可以向輸出文件夾發(fā)送一個文件,或者向 javascript 包注入一個 DataURI。無論哪種方式,它們都會給出一個 URL 來工作。

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

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

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

原生 Worker 支持

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

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

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

URIs

Webpack 5 支持在請求中處理協(xié)議。

  • 支持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。

異步模塊

Webpack 5 支持所謂的 "異步模塊"。這些模塊并不是同步解析的,而是基于異步和 Promise 的。

通過 "import "導入它們會被自動處理,不需要額外的語法,而且?guī)缀蹩床怀鰠^(qū)別。

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

在 webpack 中,有多種方式來擁有異步模塊。

  • 異步的外部資源(async externals)
  • 新規(guī)范中的 WebAssembly 模塊
  • 使用頂層 Await 的 ECMAScript 模塊。

外部資源

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

promise: 一個評估為 Promise 的表達式。外部模塊是一個異步模塊,解析值作為模塊導出使用。

import。原生的 import() 用于加載指定的請求,外部模塊是一個異步模塊,解析值作為模塊導出。外部模塊是一個異步模塊。

module: 尚未實現(xiàn),但計劃通過 import x from "..." 加載模塊。

script: 通過 `` 標簽加載一個 url,并從一個全局變量(以及它的可選屬性)中獲取輸出。外部模塊是一個異步模塊。

全新的 Node.js 生態(tài)特性

現(xiàn)在支持 package.json 中的 exportsimports 字段?,F(xiàn)在起原生支持 Yarn PnP。

更多細節(jié)請參見package exports。

開發(fā)體驗的提升

經過優(yōu)化的構建目標(target)

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

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

  • 代碼塊加載機制,以及
  • 支持的語法,如箭頭函數

統(tǒng)計

改進了統(tǒng)計測試格式的可讀性和冗余性。改進了默認值,使其不那么冗長,也適合大型構建。

進度

ProgressPlugin插件也做了一些優(yōu)化,現(xiàn)在不僅可以統(tǒng)計模塊編譯的進度,也可以統(tǒng)計 入口依賴。并且,之前展示進度可能會對構建性能有一定的影響,這次的升級也做了一些性能方面的優(yōu)化。

自動添加唯一命名

在 webpack 4 中,多個 webpack 運行時可能會在同一個 HTML 頁面上發(fā)生沖突,因為它們使用同一個全局變量進行代碼塊加載。為了解決這個問題, 需要為 output.jsonpFunction 配置提供一個自定義的名稱。

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

這個值用于使所有潛在的沖突的全局變量成為唯一。

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

自動添加公共路徑

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

Typescript 類型

Webpack 5 從源碼中生成 typescript 類型,并通過 npm 包暴露它們。

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

構建優(yōu)化

嵌套的 tree-shaking

webpack 現(xiàn)在能夠跟蹤對導出的嵌套屬性的訪問。這可以改善重新導出命名空間 對象時的 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。

內部模塊 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??梢苑治鲆韵聵擞洠?/p>

  • 函數聲明

  • 類聲明

  • 默認導出export default 或定義變量以下的:

    • 函數表達式
    • 類表達式
    • 順序表達式
    • /*#__PURE__*/ 表達式
    • 局部變量
    • 引入的捆綁(bindings)

反饋:如果你發(fā)現(xiàn)這個分析中缺少什么,請報告一個問題,我們會考慮增加它。

使用 eval() 將為一個模塊放棄這個優(yōu)化,因為經過 eval 的代碼可以引用范圍內的任何標記。這種優(yōu)化也被稱為深度范圍分析。

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 會放棄,并且完全不跟蹤這些模塊的導出信息(出于性能考慮)。

開發(fā)與生產的一致性問題

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

Webpack 5 默認在兩種模式下都啟用了 "sideEffects "優(yōu)化。在 webpack 4 中,由于 package.json 中的"sideEffects"標記不正確,這種優(yōu)化導致了一些只在生產模式下出現(xiàn)的錯誤。在開發(fā)過程中啟用這個優(yōu)化可以更快更容易地發(fā)現(xiàn)這些問題。

在很多情況下,開發(fā)和生產都是在不同的操作系統(tǒng)上進行的,文件系統(tǒng)的大小寫敏感度不同,所以 webpack 5 增加了一些奇怪的大小寫的警告/錯誤。

改進 target 配置

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

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

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

有一個目標 "browserslist",它將使用 browserslist 類庫的數據來確定環(huán)境的屬性。當項目中存在可用的 browserslist 配置時,這個目標也會被默認使用。當沒有可用的配置時,默認使用 "web"目標。

代碼塊拆分與模塊大小

現(xiàn)在模塊的尺寸比單一的數字更好的表達方式?,F(xiàn)在有不同類型的大小。

SplitChunksPlugin 現(xiàn)在知道如何處理這些不同的大小,并將它們用于 minSizemaxSize。默認情況下,只有 javascript 大小被處理,但你現(xiàn)在可以傳遞多個值來管理它們:

module.exports = {
  optimization: {
    splitChunks: {
      minSize: {
        javascript: 30000,
        webassembly: 50000,
      },
    },
  },
};

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

mini-css-extract-plugin 使用 css/mini-extra 作為大小類型,并將此大小類型自動添加到默認類型中。

還有其它的一些構建優(yōu)化,比如單個運行時的改進、模塊合并、通用 Tree Shaking 改進、個別生成代碼的改進、請參閱詳情的 webpack 5 發(fā)布資訊。

性能優(yōu)化

持久緩存

現(xiàn)在有一個文件系統(tǒng)緩存。它是可選的,可以通過以下配置啟用:

module.exports = {
  cache: {
    // 1. 將緩存類型設置為文件系統(tǒng)
    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: []以剔除該優(yōu)化。當使用 Yarn PnP 時,webpack 假設 yarn 緩存是不可改變的(通常是這樣)。你可以使用 snapshot.immutablePaths: [] 來退出這個優(yōu)化。

緩存將默認存儲在 node_modules/.cache/webpack(當使用 node_modules 時)或 .yarn/.cache/webpack(當使用 Yarn PnP 時)中。當所有的插件都正確處理緩存時,你可能永遠都不需要手動刪除它。

許多內部插件也會使用持久性緩存。例如 SourceMapDevToolPlugin (緩存 SourceMap 的生成)或ProgressPlugin (緩存模塊數量)

持久性緩存將根據使用情況自動創(chuàng)建多個緩存文件,以優(yōu)化對緩存的讀寫訪問。

默認情況下,時間戳將用于開發(fā)模式的快照,而文件哈希將用于生產模式。文件哈希也允許在 CI 中使用持久性緩存。

編譯器閑置和關閉

編譯器現(xiàn)在需要在使用后關閉。編譯器現(xiàn)在會進入和離開空閑狀態(tài),并且有這些狀態(tài)的鉤子。插件可能會使用這些鉤子來做不重要的工作。(即將持久緩存緩慢地將緩存存儲到磁盤上)。在編譯器關閉時--所有剩余的工作應該盡可能快地完成。一個回調標志著關閉完成。

插件和它們各自的作者應該預料到,有些用戶可能會忘記關閉編譯器。所以,所有的工作最終也應該在空閑狀態(tài)下完成。當工作正在進行時,應該防止進程退出。

webpack() 用法在被傳遞回調時自動調用close。

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

文件生成

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

增加了持久性緩存后,即使在重啟 webpack 進程時,也應該會有類似監(jiān)聽的體驗,但如果認為即使在 webpack 不運行時也沒有其他東西改變輸出目錄,那這個假設就太強了。

所以 webpack 現(xiàn)在會檢查輸出目錄中現(xiàn)有的文件,并將其內容與內存中的輸出文件進行比較。只有當文件被改變時,它才會寫入文件。這只在第一次構建時進行。任何增量構建都會在運行中的 webpack 進程中生成新的資產時寫入文件。

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

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

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

單一文件目標的代碼分割

只允許啟動單個文件的目標(如 node、WebWorker、electron main)現(xiàn)在支持運行時自動加載引導所需的依賴代碼片段。

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

請注意,如果目標的代碼塊加載是異步的,這使得初始評估也是異步的。當使用 output.library時,這可能是一個問題,因為現(xiàn)在導出的值是一個 Promise。

更新了解析器

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

  • 追蹤更多的依賴關系,比如丟失的文件。
  • 別名可能有多種選擇
  • 現(xiàn)在可以別名為 false 了。
  • 支持 exportsimports 字段等功能。
  • 性能提高

沒有 JS 的代碼塊

不包含 JS 代碼的塊,將不再生成 JS 文件。這就允許有只包含 CSS 的代碼塊。

重大變更:未來計劃

實驗特性

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

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

以下的實驗功能將隨 webpack 5 一起發(fā)布。

  • 舊的 WebAssembly 支持,就像 webpack 4 一樣 (experiments.syncWebAssembly)

  • 根據更新的規(guī)范(experiments.asyncWebAssembly),新增 WebAssembly 支持。

    • 這使得一個 WebAssembly 模塊成為一個異步模塊。

  • 頂層的 Await第三階段提案(experiments.topLevelAwait)

    • 在頂層使用 await 使該模塊成為一個異步模塊。

  • 以模塊的形式生成代碼包 (experiments.outputModule)

    • 這就從代碼包中移除了包裝器 IIFE,執(zhí)行嚴格模式,通過 `` 進行懶惰加載,并在模塊模式下最小化壓縮。

請注意,這也意味著 WebAssembly 的支持現(xiàn)在被默認禁用。

最小 Node.js 版本

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

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

主要的內部架構變更

這部分內容主要是那些想貢獻 webpack 內核,以及加載器、插件開發(fā)者需要密切關注的。如果你只是使用 webpack,可以忽略這部分。內容非常多,而且比較難懂。

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

新的插件運行順序

現(xiàn)在 webpack 5 中的插件在應用配置默認值之前就會被應用。這使得插件可以應用自己的默認值,或者作為配置預設。但這也是一個突破性的變化,因為插件在應用時不能依賴配置值的設置。

遷移:只在插件鉤子中訪問配置?;蛘咦詈猛耆苊庠L問配置,并通過構造函數獲取選項。

運行時模塊

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

在大多數情況下,核心運行代碼時允許內聯(lián)入口模塊,而不是用 __webpack_require__ 來調用它。如果代碼包中沒有其他模塊,則根本不需要使用__webpack_require__。這與模塊合并很好地結合在一起,即多個模塊被合并成一個模塊。在最好的情況下,根本不需要運行時代碼。

遷移:如果你在插件中注入運行時代碼到 webpack 運行時,可以考慮使用 RuntimeModules 來代替。

序列化

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

遷移:當使用自定義模塊或依賴關系時,建議將它們實現(xiàn)成可序列化的,以便從持久化緩存中獲益。

用于緩存的插件

增加了一個帶有插件接口的 Cache 類。該類可用于寫入和讀取緩存。根據配置的不同,不同的插件可以為緩存添加功能。MemoryCachePlugin 增加了內存緩存功能。FileCachePlugin 增加了持久性(文件系統(tǒng))緩存。FileCachePlugin 使用序列化機制將緩存項目持久化到磁盤上或從磁盤上恢復。

Tapable 插件升級

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

遷移:使用新的 tapable API。

Main/Chunk/ModuleTemplate 廢棄

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

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

依然可以侵入部分模板。鉤子現(xiàn)在在 JavascriptModulesPlugin 中,而不是 Main/ChunkTemplate 中。(是的,插件也可以有鉤子,我稱之為附加鉤子。)有一個兼容層,所以 Main/Chunk/ModuleTemplate 仍然存在,但只是將 tap 調用委托給新的鉤子位置。

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

入口文件的新增配置

在 webpack 5 中,入口文件除了字符串、字符串數組,也可以使用描述符進行配置了,如:

module.exports = {
  entry: {
    catalog: {
      import: './catalog.js',
    },
  },
};

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

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

另外,入口文件的配置,新增了文件依賴定義、生成類庫的格式類型(commonjs 或 amd),也可以設置運行時的名字,以及代碼塊加載的方式,更多細節(jié)可以參考完整的發(fā)布記錄。

排序與 ID

webpack 曾經在編譯階段以特定的方式對模塊和代碼塊進行排序,以遞增的方式分配 ID?,F(xiàn)在不再是這樣了。順序將不再用于 ID 的生成,取而代之的是,ID 生成的完全控制在插件中。優(yōu)化模塊和代碼塊順序的鉤子已經被移除。

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

從數組到集合(Set)

  • Compilation.modules 現(xiàn)在是一個集合
  • Compilation.chunks 現(xiàn)在是一個集合
  • Chunk.files 現(xiàn)在是一個集合

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

遷移: 使用集合方法代替數組方法。

文件系統(tǒng)與信息變更

webpack 5 中,一個是需要使用 Compilation.fileSystemInfo 替代file/contextTimestamps,獲取文件的時間戳信息,另一個是新增Compiler.modifiedFiles 以便更容易引用更改后的文件。

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

模塊熱替換

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

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

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

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

工作隊列

webpack 曾經通過函數調用函數的形式來進行模塊處理,還有一個 semaphore 選項限制并行性。Compilation.semaphore 已被移除,現(xiàn)在可以使用異步隊列處理,每個步驟都有獨立的隊列:

  • Compilation.factorizeQueue:為一組 dependencies 調用模塊工廠。
  • Compilation.addModuleQueue:將模塊添加到編譯隊列中(可以使用緩存恢復模塊)
  • Compilation.buildQueue:必要時構建模塊(可將模塊存儲到緩存中)
  • Compilation.rebuildQueue:如需手動觸發(fā),則會重新構建模塊
  • Compilation.processDependenciesQueue:處理模塊的 dependencies。

這些隊列會有一些 hook 來監(jiān)聽并攔截工作的進程。未來,多個編譯器會同時工作,可以通過攔截這些隊列來進行編譯工作的編排。

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

模塊和 chunk 圖

webpack 曾經在依賴關系中存儲了已解析的模塊,并在 chunk 中存儲引入的模塊。但現(xiàn)已發(fā)生變化。所有關于模塊在模塊圖中如何連接的信息,現(xiàn)在都存儲在 ModulGraph 的 class 中。所有關于模塊與 chunk 如何連接的信息現(xiàn)在都已存儲在 ChunkGraph 的 class 中。依賴于 chunk 圖的信息也存儲在相關的 class 中。

以下列舉一些模塊的信息已被移動的例子:

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

當從緩存中恢復模塊時,webpack 會將模塊從圖中斷開?,F(xiàn)在已無需這么做。一個模塊不存儲圖形的任何信息,技術上可以在多個圖形中使用。這會使得緩存變得更加容易。這部分變化中大多數都有一個適配層,當使用時,它會打印一個棄用警告。

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

模塊 Source Types

Modules 現(xiàn)在必須通過 Module.getSourceTypes() 來定義它們支持的源碼類型。根據這一點,不同的插件會用這些類型調用 source()。對于源類型為 javascriptJavascriptModulesPlugin 會將源代碼嵌入到 bundle 中。源類型 webassemblyWebAssemblyModulesPlugin 會 emit 一個 wasm 文件。同時,也支持自定義源類型,例如,mini-css-extract-plugin 會使用源類型為 stylesheet 將源碼嵌入到 css 文件中。

模塊類型與源類型間沒有關系。即使模塊類型為 json,也可以使用源類型為 javascript 和模塊類型為 webassembly/experimentaljavascriptwebassembly。

遷移:自定義模塊需要實現(xiàn)這些新的接口方法。

全新的觀察者

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

它還能在監(jiān)聽時捕捉更多關于文件系統(tǒng)的信息。目前,它還可以捕獲 mtimes 和監(jiān)視事件時間,以及丟失文件的信息。為此,WatchFileSystem API 做了一點小改動。在修改的同時,我們還將 Arrays 轉換為 Sets,Objects 轉換為 Maps。

SizeOnlySource after emit

webpack 現(xiàn)在使用 SizeOnlySource 替換 Compilation.assets 中的 Sources,以減少內存占用。

ExportsInfo

重構了模塊導出信息的存儲方式。ModuleGraph 現(xiàn)在為每個 Module 提供了一個 ExportsInfo,它用于存儲每個 export 的信息。如果模塊僅以副作用的方式使用,它還存儲了關于未知 export 的信息,

對于每個 export,都會存儲以下信息:

  • 是否使用 export? 是否使用并不確定。(詳見 optimization.usedExports

  • 是否提供 export? 是否提供并不確定。(詳見 optimization.providedExports

  • 能否重命名 export 名? 是否重命名,也不確定

  • 如果 export 已重新命名,則為新名稱。(詳見 optimization.mangleExports

  • 嵌套的 ExportsInfo,如果 export 是一個含有附加信息的對象,那么它本身就是一個對象

    • 用于重新導出命名空間對象:import * as X from "..."; export { X };
    • 用于表示 JSON 模塊中的結構

代碼生成階段

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

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

依賴關系參考

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

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

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

Presentational Dependencies

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

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

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

棄用 loaders

  • null-loader

已被棄用。使用

  module.exports = {
    resolve: {
      alias: {
        xyz$: false,
      },
    },
  };

或者使用絕對路徑

  module.exports = {
    resolve: {
      alias: {
        [path.resolve(__dirname, '....')]: false,
      },
    },
  };

總結

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

官網:https://webpack.js.org 中文文檔:https://webapck.docschina.org

以上就是W3Cschool編程獅關于闊別兩年,webpack 5 正式發(fā)布了!的相關介紹了,希望對大家有所幫助。

0 人點贊