HI,繼 Vue 3 和 React 17 之后,我又來啦!印記中文已經(jīng)完成了 webpack v5 中文文檔的同步及翻譯工作,大家可以無縫進(jìn)行閱讀哦。
文檔地址請認(rèn)準(zhǔn):https://webpack.docschina.org
文檔地址請認(rèn)準(zhǔn):https://webpack.docschina.org
文檔地址請認(rèn)準(zhǔn):https://webpack.docschina.org
重要的事說三遍,我們的文檔隸屬于官方,我們沒有其他的域名哦,并且是與官方進(jìn)行實(shí)時(shí)同步。
2020 年 10 月 10 日,webpack 升級至 5.0 版本,并且為官網(wǎng)添加了博客目錄。我們及時(shí)的進(jìn)行了同步,此文是我們閱讀后總結(jié)歸納的版本。話不多說開始正文。
自從 2018 年 2 月,webpack4 發(fā)布以來,webpack 就暫時(shí)沒有更進(jìn)一步的重大更新,為了保持 API 的一致性,舊的架構(gòu)沒有做太多改變,遺留了很多的包袱。闊別 2 年多后,2020 年 10 月 10 日,webpack 5 正式發(fā)布,并帶來了諸多重大的變更,將會使前端工程師的構(gòu)建效率與質(zhì)量大為提升。
本次重大發(fā)布的整體發(fā)展方向如下:
- 嘗試用持久性緩存來提高構(gòu)建性能。
- 嘗試用更好的算法和默認(rèn)值來改進(jìn)長期緩存。
- 嘗試用更好的 Tree Shaking 和代碼生成來改善包大小。
- 嘗試改善與網(wǎng)絡(luò)平臺的兼容性。
- 嘗試在不引入任何破壞性變化的情況下,
-
- 清理那些在實(shí)現(xiàn) v4 功能時(shí)處于奇怪狀態(tài)的內(nèi)部結(jié)構(gòu)。
- 試圖通過現(xiàn)在引入突破性的變化來為未來的功能做準(zhǔn)備,
-
- 盡可能長時(shí)間地保持在 v5 版本上。
webpack 5
的 Release Note
非常長,本文嘗試摘出最簡練的信息。
功能清除
清理已棄用的功能
所有在 webpack 4
標(biāo)記即將過期的功能,都已在該版移除。因此在遷移到 webpack 5
之前,請確保你在 webpack 4
運(yùn)行的構(gòu)建不會有任何的功能過期警告。
不再為 Node.js 模塊 自動引用 Polyfills
不再為 Node.js
內(nèi)置模塊自動添加 Polyfills
。任何項(xiàng)目中有引用 Node.js
內(nèi)置模塊,在 webpack 4
或之前的版本中會自動添加 Polyfills
。但 webpack 5
將不會再這樣做,webpack
會投入更多的精力到前端模塊的兼容性工作中。
如果你的代碼中有引用這些 Node.js
的模塊,要升級到 webpack 5
, 將盡量使用前端的模塊,或者自行手動添加適合的 Polyfills
。
而針對那些類庫的開發(fā)者,請?jiān)?package.json
中定義 browser
字段,使類庫在前端能適用。
針對長期緩存的優(yōu)化
確定的 Chunk、模塊 ID 和導(dǎo)出名稱
新增了長期緩存的算法。這些算法在生產(chǎn)模式下是默認(rèn)啟用的。
chunkIds: "deterministic"``moduleIds: "deterministic"``mangleExports: "deterministic"
該算法以確定性的方式為模塊和分塊分配短的(3 或 5 位)數(shù)字 ID,這是包大小和長期緩存之間的一種權(quán)衡。由于這些配置將使用確定的 ID 和名稱,這意味著生成的緩存失效不再更頻繁。
真正的內(nèi)容哈希
當(dāng)使用[contenthash]
時(shí),Webpack 5 將使用真正的文件內(nèi)容哈希值。之前它 "只 "使用內(nèi)部結(jié)構(gòu)的哈希值。當(dāng)只有注釋被修改或變量被重命名時(shí),這對長期緩存會有積極影響。這些變化在壓縮后是不可見的。
更好的開發(fā)支持
命名代碼塊 ID
在開發(fā)模式下,默認(rèn)啟用的新命名代碼塊 ID 算法為模塊(和文件名)提供了人類可讀的名稱。模塊 ID 由其路徑?jīng)Q定,相對于 context
。代碼塊 ID 由代碼塊的內(nèi)容決定。
所以你不再需要使用import(/* webpackChunkName: "name" */ "module")
來調(diào)試。但如果你想控制生產(chǎn)環(huán)境的文件名,還是有意義的。
可以在生產(chǎn)環(huán)境中使用 chunkIds: "named"
在生產(chǎn)環(huán)境中使用,但要確保不要不小心暴露模塊名的敏感信息。
遷移:如果你不喜歡在開發(fā)中改變文件名,你可以通過 chunkIds: "natural"
來使用舊的數(shù)字模式。
模塊聯(lián)邦
Webpack 5 增加了一個(gè)新的功能 "模塊聯(lián)邦",它允許多個(gè) webpack 構(gòu)建一起工作。從運(yùn)行時(shí)的角度來看,多個(gè)構(gòu)建的模塊將表現(xiàn)得像一個(gè)巨大的連接模塊圖。從開發(fā)者的角度來看,模塊可以從指定的遠(yuǎn)程構(gòu)建中導(dǎo)入,并以最小的限制來使用。
支持嶄新的 Web 平臺功能
對于 Web 平臺的的一些支持 ,Webpack 5 也做了更好的完善更新。
JSON 模塊
比如對 JSON 模塊,會與現(xiàn)在的提案保持一致,并且要求進(jìn)行默認(rèn)的導(dǎo)出,否則會有警告信息。即使使用默認(rèn)導(dǎo)出,未使用的屬性也會被 optimization.usedExports
優(yōu)化丟棄,屬性會被 optimization.mangleExports
優(yōu)化打亂。
如果想用自定義的 JSON 解析器,可以在 Rule.parser.parse
中指定一個(gè)自定義的 JSON 解析器來導(dǎo)入類似 JSON 的文件(例如針對 toml、yaml、json5 等)。
資源模塊
Webpack 5 現(xiàn)在已經(jīng)對表示資源的模塊提供了內(nèi)置支持。這些模塊可以向輸出文件夾發(fā)送一個(gè)文件,或者向 javascript 包注入一個(gè) DataURI。無論哪種方式,它們都會給出一個(gè) URL 來工作。
它們可以通過多種方式被使用:
import url from "./image.png"
和 在module.rule
中設(shè)置type: "asset"
當(dāng)匹配這樣的導(dǎo)入時(shí)。(老方法)new URL("./image.png", import.meta.url)
(新方式)
選擇 "新的方式 "語法是為了允許在沒有打包工具的情況下運(yùn)行代碼。這種語法也可以在瀏覽器中的原生 ECMAScript 模塊中使用。
原生 Worker 支持
當(dāng)把資源的 new URL
和 new Worker
/new SharedWorker
/navigator.serviceWorker.register
結(jié)合起來時(shí),webpack 會自動為 web worker 創(chuàng)建一個(gè)新的入口點(diǎn)(entrypoint)。
new Worker(new URL("./worker.js", import.meta.url))
選擇這種語法也是為了允許在沒有打包工具的情況下運(yùn)行代碼。這種語法在瀏覽器的原生 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()
選擇加入。
-
- 默認(rèn)情況下,當(dāng)目標(biāo)為 "web "時(shí),這些 URI 會導(dǎo)致對外部資源的請求(它們是外部資源)。
支持請求中的片段。例如:./file.js#fragment
。
異步模塊
Webpack 5 支持所謂的 "異步模塊"。這些模塊并不是同步解析的,而是基于異步和 Promise 的。
通過 "import "導(dǎo)入它們會被自動處理,不需要額外的語法,而且?guī)缀蹩床怀鰠^(qū)別。
通過require()
導(dǎo)入它們會返回一個(gè)解析到導(dǎo)出的 Promise。
在 webpack 中,有多種方式來擁有異步模塊。
- 異步的外部資源(async externals)
- 新規(guī)范中的 WebAssembly 模塊
- 使用頂層 Await 的 ECMAScript 模塊。
外部資源
Webpack 5 增加了更多的外部類型來覆蓋更多的應(yīng)用:
promise
: 一個(gè)評估為 Promise 的表達(dá)式。外部模塊是一個(gè)異步模塊,解析值作為模塊導(dǎo)出使用。
import
。原生的 import()
用于加載指定的請求,外部模塊是一個(gè)異步模塊,解析值作為模塊導(dǎo)出。外部模塊是一個(gè)異步模塊。
module
: 尚未實(shí)現(xiàn),但計(jì)劃通過 import x from "..."
加載模塊。
script
: 通過 `` 標(biāo)簽加載一個(gè) url,并從一個(gè)全局變量(以及它的可選屬性)中獲取輸出。外部模塊是一個(gè)異步模塊。
全新的 Node.js 生態(tài)特性
現(xiàn)在支持 package.json 中的 exports
和 imports
字段?,F(xiàn)在起原生支持 Yarn PnP。
更多細(xì)節(jié)請參見package exports。
開發(fā)體驗(yàn)的提升
經(jīng)過優(yōu)化的構(gòu)建目標(biāo)(target)
Webpack 5 允許傳遞一個(gè)目標(biāo)列表,并且支持目標(biāo)的版本。例如 target: "node14"``target: ["web", "es2020"]
。
這是一個(gè)簡單的方法,為 webpack 提供它需要確定的所有信息:
- 代碼塊加載機(jī)制,以及
- 支持的語法,如箭頭函數(shù)
統(tǒng)計(jì)
改進(jìn)了統(tǒng)計(jì)測試格式的可讀性和冗余性。改進(jìn)了默認(rèn)值,使其不那么冗長,也適合大型構(gòu)建。
進(jìn)度
ProgressPlugin
插件也做了一些優(yōu)化,現(xiàn)在不僅可以統(tǒng)計(jì)模塊編譯的進(jìn)度,也可以統(tǒng)計(jì) 入口
和 依賴
。并且,之前展示進(jìn)度可能會對構(gòu)建性能有一定的影響,這次的升級也做了一些性能方面的優(yōu)化。
自動添加唯一命名
在 webpack 4 中,多個(gè) webpack 運(yùn)行時(shí)可能會在同一個(gè) HTML 頁面上發(fā)生沖突,因?yàn)樗鼈兪褂猛粋€(gè)全局變量進(jìn)行代碼塊加載。為了解決這個(gè)問題, 需要為 output.jsonpFunction
配置提供一個(gè)自定義的名稱。
Webpack 5 確實(shí)會從 package.json
name
中自動推斷出一個(gè)唯一的構(gòu)建名稱,并將其作為 output.uniqueName
的默認(rèn)值。
這個(gè)值用于使所有潛在的沖突的全局變量成為唯一。
遷移: 由于 package.json
中有唯一的名稱,可將 output.jsonpFunction
刪除。
自動添加公共路徑
Webpack 5 會在可能的情況下自動確定 output.publicPath
。
Typescript 類型
Webpack 5 從源碼中生成 typescript 類型,并通過 npm 包暴露它們。
遷移:刪除 @types/webpack
。當(dāng)名稱不同時(shí)更新引用。
構(gòu)建優(yōu)化
嵌套的 tree-shaking
webpack 現(xiàn)在能夠跟蹤對導(dǎo)出的嵌套屬性的訪問。這可以改善重新導(dǎo)出命名空間 對象時(shí)的 Tree Shaking(清除未使用的導(dǎo)出和混淆導(dǎo)出)。
// 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);
在這個(gè)例子中,可以在生產(chǎn)模式下刪除導(dǎo)出的b
。
內(nèi)部模塊 tree-shaking
webpack 4 沒有分析模塊的導(dǎo)出和引用之間的依賴關(guān)系。webpack 5 有一個(gè)新的選項(xiàng) optimization.innerGraph
,在生產(chǎn)模式下是默認(rèn)啟用的,它可以對模塊中的標(biāo)志進(jìn)行分析,找出導(dǎo)出和引用之間的依賴關(guān)系。
在這樣的模塊中:
import { something } from './something';
function usingSomething() {
return something;
}
export function test() {
return usingSomething();
}
內(nèi)部依賴圖算法會找出 something
只有在使用 test
導(dǎo)出時(shí)才會使用。這允許將更多的出口標(biāo)記為未使用,并從代碼包中省略更多的代碼。
當(dāng)設(shè)置"sideEffects": false
時(shí),可以省略更多的模塊。在這個(gè)例子中,當(dāng) test
導(dǎo)出未被使用時(shí),./something
將被省略。
要獲得未使用的導(dǎo)出信息,需要使用 optimization.unusedExports
。要刪除無副作用的模塊,需要使用optimization.sideEffects
??梢苑治鲆韵聵?biāo)記:
- 函數(shù)聲明
- 類聲明
默認(rèn)導(dǎo)出export default
或定義變量以下的:
-
- 函數(shù)表達(dá)式
- 類表達(dá)式
- 順序表達(dá)式
/*#__PURE__*/
表達(dá)式- 局部變量
- 引入的捆綁(bindings)
反饋:如果你發(fā)現(xiàn)這個(gè)分析中缺少什么,請報(bào)告一個(gè)問題,我們會考慮增加它。
使用 eval()
將為一個(gè)模塊放棄這個(gè)優(yōu)化,因?yàn)榻?jīng)過 eval 的代碼可以引用范圍內(nèi)的任何標(biāo)記。這種優(yōu)化也被稱為深度范圍分析。
CommonJs Tree Shaking
webpack 曾經(jīng)不進(jìn)行對 CommonJs 導(dǎo)出和 require()
調(diào)用時(shí)的導(dǎo)出使用分析。
webpack 5 增加了對一些 CommonJs 構(gòu)造的支持,允許消除未使用的 CommonJs 導(dǎo)出,并從 require()
調(diào)用中跟蹤引用的導(dǎo)出名稱。
支持以下構(gòu)造:
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 導(dǎo)入
require()
一個(gè) ESM 模塊
- 被標(biāo)記的導(dǎo)出類型 (對非嚴(yán)格 ESM 導(dǎo)入做特殊處理):
-
Object.defineProperty(exports|this|module.exports, "__esModule", { value: true|!0 })
exports|this|module.exports.__esModule = true|!0
- 未來計(jì)劃支持更多的構(gòu)造
當(dāng)檢測到不可分析的代碼時(shí),webpack 會放棄,并且完全不跟蹤這些模塊的導(dǎo)出信息(出于性能考慮)。
開發(fā)與生產(chǎn)的一致性問題
我們試圖通過改善兩種模式的相似性,在開發(fā)模式的構(gòu)建性能和避免僅在生產(chǎn)模式的產(chǎn)生的問題之間找到一個(gè)很好的平衡點(diǎn)。
Webpack 5 默認(rèn)在兩種模式下都啟用了 "sideEffects "優(yōu)化。在 webpack 4 中,由于 package.json 中的"sideEffects"
標(biāo)記不正確,這種優(yōu)化導(dǎo)致了一些只在生產(chǎn)模式下出現(xiàn)的錯(cuò)誤。在開發(fā)過程中啟用這個(gè)優(yōu)化可以更快更容易地發(fā)現(xiàn)這些問題。
在很多情況下,開發(fā)和生產(chǎn)都是在不同的操作系統(tǒng)上進(jìn)行的,文件系統(tǒng)的大小寫敏感度不同,所以 webpack 5 增加了一些奇怪的大小寫的警告/錯(cuò)誤。
改進(jìn) target
配置
在 webpack 4 中,"target "是在 "web"
和 "node"
之間的一個(gè)粗略的選擇(還有一些其他的)。Webpack 5 給你更多的選擇。target
選項(xiàng)現(xiàn)在比以前影響了更多關(guān)于生成代碼的事情,比如代碼塊加載方法、代碼塊的格式、externals
是否默認(rèn)被啟用等等。
此外,對于其中的一些情況,在 "web"
和 "node"
之間的選擇過于粗略,我們需要更多的信息。因此,我們允許指定最低版本,例如 "node10.13"
,并推斷出更多關(guān)于目標(biāo)環(huán)境的屬性。
現(xiàn)在也允許用一個(gè)數(shù)組組合多個(gè)目標(biāo),webpack 將確定所有目標(biāo)的最小屬性。使用數(shù)組也很有用,當(dāng)使用像 "web"
或 "node"
這樣沒有提供完整信息的目標(biāo)時(shí)(沒有版本號)。例如,["web", "es2020"]
結(jié)合了這兩個(gè)部分目標(biāo)。
有一個(gè)目標(biāo) "browserslist"
,它將使用 browserslist 類庫的數(shù)據(jù)來確定環(huán)境的屬性。當(dāng)項(xiàng)目中存在可用的 browserslist 配置時(shí),這個(gè)目標(biāo)也會被默認(rèn)使用。當(dāng)沒有可用的配置時(shí),默認(rèn)使用 "web"
目標(biāo)。
代碼塊拆分與模塊大小
現(xiàn)在模塊的尺寸比單一的數(shù)字更好的表達(dá)方式?,F(xiàn)在有不同類型的大小。
SplitChunksPlugin 現(xiàn)在知道如何處理這些不同的大小,并將它們用于 minSize
和 maxSize
。默認(rèn)情況下,只有 javascript
大小被處理,但你現(xiàn)在可以傳遞多個(gè)值來管理它們:
module.exports = {
optimization: {
splitChunks: {
minSize: {
javascript: 30000,
webassembly: 50000,
},
},
},
};
你仍然可以使用一個(gè)數(shù)字來表示大小。在這種情況下,webpack 會自動使用默認(rèn)的大小類型。
mini-css-extract-plugin
使用 css/mini-extra
作為大小類型,并將此大小類型自動添加到默認(rèn)類型中。
還有其它的一些構(gòu)建優(yōu)化,比如單個(gè)運(yùn)行時(shí)的改進(jìn)、模塊合并、通用 Tree Shaking 改進(jìn)、個(gè)別生成代碼的改進(jìn)、請參閱詳情的 webpack 5 發(fā)布資訊。
性能優(yōu)化
持久緩存
現(xiàn)在有一個(gè)文件系統(tǒng)緩存。它是可選的,可以通過以下配置啟用:
module.exports = {
cache: {
// 1. 將緩存類型設(shè)置為文件系統(tǒng)
type: 'filesystem',
buildDependencies: {
// 2. 將你的 config 添加為 buildDependency,以便在改變 config 時(shí)獲得緩存無效
config: [__filename],
// 3. 如果你有其他的東西被構(gòu)建依賴,你可以在這里添加它們
// 注意,webpack、加載器和所有從你的配置中引用的模塊都會被自動添加
},
},
};
重要說明:
默認(rèn)情況下,webpack 假定 webpack 所在的 node_modules
目錄只被包管理器修改。對 node_modules
來說,哈希值和時(shí)間戳?xí)惶^。出于性能考慮,只使用包名和版本。只要不指定resolve.symlinks: false
,Symlinks(即npm/yarn link
)就沒有問題(無論如何都要避免)。不要直接編輯node_modules
中的文件,除非你用 snapshot.managedPaths: []
以剔除該優(yōu)化。當(dāng)使用 Yarn PnP 時(shí),webpack 假設(shè) yarn 緩存是不可改變的(通常是這樣)。你可以使用 snapshot.immutablePaths: []
來退出這個(gè)優(yōu)化。
緩存將默認(rèn)存儲在 node_modules/.cache/webpack
(當(dāng)使用 node_modules 時(shí))或 .yarn/.cache/webpack
(當(dāng)使用 Yarn PnP 時(shí))中。當(dāng)所有的插件都正確處理緩存時(shí),你可能永遠(yuǎn)都不需要手動刪除它。
許多內(nèi)部插件也會使用持久性緩存。例如 SourceMapDevToolPlugin
(緩存 SourceMap 的生成)或ProgressPlugin
(緩存模塊數(shù)量)
持久性緩存將根據(jù)使用情況自動創(chuàng)建多個(gè)緩存文件,以優(yōu)化對緩存的讀寫訪問。
默認(rèn)情況下,時(shí)間戳將用于開發(fā)模式的快照,而文件哈希將用于生產(chǎn)模式。文件哈希也允許在 CI 中使用持久性緩存。
編譯器閑置和關(guān)閉
編譯器現(xiàn)在需要在使用后關(guān)閉。編譯器現(xiàn)在會進(jìn)入和離開空閑狀態(tài),并且有這些狀態(tài)的鉤子。插件可能會使用這些鉤子來做不重要的工作。(即將持久緩存緩慢地將緩存存儲到磁盤上)。在編譯器關(guān)閉時(shí)--所有剩余的工作應(yīng)該盡可能快地完成。一個(gè)回調(diào)標(biāo)志著關(guān)閉完成。
插件和它們各自的作者應(yīng)該預(yù)料到,有些用戶可能會忘記關(guān)閉編譯器。所以,所有的工作最終也應(yīng)該在空閑狀態(tài)下完成。當(dāng)工作正在進(jìn)行時(shí),應(yīng)該防止進(jìn)程退出。
webpack()
用法在被傳遞回調(diào)時(shí)自動調(diào)用close
。
遷移:在使用 Node.js API 時(shí),一定要在完成工作后調(diào)用 Compiler.close
。
文件生成
webpack 過去總是在第一次構(gòu)建時(shí)發(fā)出所有的輸出文件,但在增量(觀察)構(gòu)建時(shí)跳過了寫入未更改的文件。假設(shè)在 webpack 運(yùn)行時(shí),沒有任何其他東西改變輸出文件。
增加了持久性緩存后,即使在重啟 webpack 進(jìn)程時(shí),也應(yīng)該會有類似監(jiān)聽的體驗(yàn),但如果認(rèn)為即使在 webpack 不運(yùn)行時(shí)也沒有其他東西改變輸出目錄,那這個(gè)假設(shè)就太強(qiáng)了。
所以 webpack 現(xiàn)在會檢查輸出目錄中現(xiàn)有的文件,并將其內(nèi)容與內(nèi)存中的輸出文件進(jìn)行比較。只有當(dāng)文件被改變時(shí),它才會寫入文件。這只在第一次構(gòu)建時(shí)進(jìn)行。任何增量構(gòu)建都會在運(yùn)行中的 webpack 進(jìn)程中生成新的資產(chǎn)時(shí)寫入文件。
我們假設(shè) webpack 和插件只有在內(nèi)容被改變時(shí)才會生成新的資產(chǎn)。應(yīng)該使用緩存來確保在輸入相同時(shí)不會生成新的資產(chǎn)。不遵循這個(gè)建議會降低性能。
被標(biāo)記為 [不可變]
的文件(包括內(nèi)容哈希),當(dāng)已經(jīng)存在一個(gè)同名文件時(shí),將永遠(yuǎn)不會被寫入。我們假設(shè)當(dāng)文件內(nèi)容發(fā)生變化時(shí),內(nèi)容哈希會發(fā)生變化。這在一般情況下是正確的,但在 webpack 或插件開發(fā)過程中可能并不總是如此。
重大變更:長期未解決的問題
單一文件目標(biāo)的代碼分割
只允許啟動單個(gè)文件的目標(biāo)(如 node、WebWorker、electron main)現(xiàn)在支持運(yùn)行時(shí)自動加載引導(dǎo)所需的依賴代碼片段。
這允許對這些目標(biāo)使用 chunks: "all"
和 optimization.runtimeChunk
。
請注意,如果目標(biāo)的代碼塊加載是異步的,這使得初始評估也是異步的。當(dāng)使用 output.library
時(shí),這可能是一個(gè)問題,因?yàn)楝F(xiàn)在導(dǎo)出的值是一個(gè) Promise。
更新了解析器
enhanced-resolve
更新到了 v5,有以下改進(jìn):
- 追蹤更多的依賴關(guān)系,比如丟失的文件。
- 別名可能有多種選擇
- 現(xiàn)在可以別名為
false
了。 - 支持
exports
和imports
字段等功能。 - 性能提高
沒有 JS 的代碼塊
不包含 JS 代碼的塊,將不再生成 JS 文件。這就允許有只包含 CSS 的代碼塊。
重大變更:未來計(jì)劃
實(shí)驗(yàn)特性
在 webpack 5 中,有一個(gè)新的 experiments
配置選項(xiàng),允許啟用實(shí)驗(yàn)性功能。這使得哪些功能被啟用/使用變得很清楚。
雖然 webpack 遵循語義版本化,但它會對實(shí)驗(yàn)性功能進(jìn)行例外處理。實(shí)驗(yàn)性功能可能會在 webpack 的次要版本中包含破壞性的變化。當(dāng)這種情況發(fā)生時(shí),我們會在變更日志中添加一個(gè)明確的注釋。這將使我們能夠更快地迭代實(shí)驗(yàn)性功能,同時(shí)也使我們能夠在主要版本上為穩(wěn)定的功能停留更長時(shí)間。
以下的實(shí)驗(yàn)功能將隨 webpack 5 一起發(fā)布。
- 舊的 WebAssembly 支持,就像 webpack 4 一樣 (
experiments.syncWebAssembly
)
- 根據(jù)更新的規(guī)范(
experiments.asyncWebAssembly
),新增 WebAssembly 支持。
-
- 這使得一個(gè) WebAssembly 模塊成為一個(gè)異步模塊。
- 頂層的 Await第三階段提案(
experiments.topLevelAwait
)
-
- 在頂層使用
await
使該模塊成為一個(gè)異步模塊。
- 在頂層使用
- 以模塊的形式生成代碼包 (
experiments.outputModule
)
-
- 這就從代碼包中移除了包裝器 IIFE,執(zhí)行嚴(yán)格模式,通過 `` 進(jìn)行懶惰加載,并在模塊模式下最小化壓縮。
請注意,這也意味著 WebAssembly 的支持現(xiàn)在被默認(rèn)禁用。
最小 Node.js 版本
最低支持的 Node.js 版本從 6 增加到 10.13.0(LTS)。
遷移:升級到最新的 Node.js 版本。
主要的內(nèi)部架構(gòu)變更
這部分內(nèi)容主要是那些想貢獻(xiàn) webpack 內(nèi)核,以及加載器、插件開發(fā)者需要密切關(guān)注的。如果你只是使用 webpack,可以忽略這部分。內(nèi)容非常多,而且比較難懂。
以下咱們來介紹一些最主要的一些內(nèi)部架構(gòu)的變更。
新的插件運(yùn)行順序
現(xiàn)在 webpack 5 中的插件在應(yīng)用配置默認(rèn)值之前就會被應(yīng)用。這使得插件可以應(yīng)用自己的默認(rèn)值,或者作為配置預(yù)設(shè)。但這也是一個(gè)突破性的變化,因?yàn)椴寮趹?yīng)用時(shí)不能依賴配置值的設(shè)置。
遷移:只在插件鉤子中訪問配置?;蛘咦詈猛耆苊庠L問配置,并通過構(gòu)造函數(shù)獲取選項(xiàng)。
運(yùn)行時(shí)模塊
大部分的運(yùn)行時(shí)代碼被移到了所謂的"運(yùn)行時(shí)模塊"中。這些特殊模塊負(fù)責(zé)添加運(yùn)行時(shí)代碼。它們可以被添加到任何塊中,但目前總是被添加到運(yùn)行時(shí)塊中。"運(yùn)行時(shí)需求"控制哪些運(yùn)行時(shí)模塊(或核心運(yùn)行時(shí)部件)被添加到代碼包中。這確保了只有使用的運(yùn)行時(shí)代碼才會被添加到代碼包中。未來,運(yùn)行時(shí)模塊也可以添加到按需加載的塊中,以便在需要時(shí)加載運(yùn)行時(shí)代碼。
在大多數(shù)情況下,核心運(yùn)行代碼時(shí)允許內(nèi)聯(lián)入口模塊,而不是用 __webpack_require__
來調(diào)用它。如果代碼包中沒有其他模塊,則根本不需要使用__webpack_require__
。這與模塊合并很好地結(jié)合在一起,即多個(gè)模塊被合并成一個(gè)模塊。在最好的情況下,根本不需要運(yùn)行時(shí)代碼。
遷移:如果你在插件中注入運(yùn)行時(shí)代碼到 webpack 運(yùn)行時(shí),可以考慮使用 RuntimeModules 來代替。
序列化
我們添加了一個(gè)序列化機(jī)制,以允許在 webpack 中對復(fù)雜對象進(jìn)行序列化。它有一個(gè)可選的語義,所以那些應(yīng)該被序列化的類需要被明確地標(biāo)記出來(并且實(shí)現(xiàn)它們的序列化)。大多數(shù)模塊、所有的依賴關(guān)系和一些錯(cuò)誤都已經(jīng)這樣做了。
遷移:當(dāng)使用自定義模塊或依賴關(guān)系時(shí),建議將它們實(shí)現(xiàn)成可序列化的,以便從持久化緩存中獲益。
用于緩存的插件
增加了一個(gè)帶有插件接口的 Cache
類。該類可用于寫入和讀取緩存。根據(jù)配置的不同,不同的插件可以為緩存添加功能。MemoryCachePlugin
增加了內(nèi)存緩存功能。FileCachePlugin
增加了持久性(文件系統(tǒng))緩存。FileCachePlugin
使用序列化機(jī)制將緩存項(xiàng)目持久化到磁盤上或從磁盤上恢復(fù)。
Tapable 插件升級
webpack 3 插件的 compat 層已經(jīng)被移除。它在 webpack 4 中已經(jīng)被取消了。一些較少使用的 tapable API 被刪除或廢棄。
遷移:使用新的 tapable API。
Main/Chunk/ModuleTemplate 廢棄
打包模板已經(jīng)重構(gòu)。MainTemplate/ChunkTemplate/ModuleTemplate 被廢棄,現(xiàn)在 JavascriptModulesPlugin 負(fù)責(zé) JS 模板。
在那次重構(gòu)之前,JS 輸出由 Main/ChunkTemplate 處理,而另一個(gè)輸出(即 WASM、CSS)則由插件處理。這樣看起來 JS 是一等公民,而其它輸出是二等。重構(gòu)改變了這一點(diǎn),所有的輸出都由他們的插件處理。
依然可以侵入部分模板。鉤子現(xiàn)在在 JavascriptModulesPlugin 中,而不是 Main/ChunkTemplate 中。(是的,插件也可以有鉤子,我稱之為附加鉤子。)有一個(gè)兼容層,所以 Main/Chunk/ModuleTemplate 仍然存在,但只是將 tap 調(diào)用委托給新的鉤子位置。
遷移:按照 deprecation 消息中的建議。主要是指向不同位置的鉤子。
入口文件的新增配置
在 webpack 5 中,入口文件除了字符串、字符串?dāng)?shù)組,也可以使用描述符進(jìn)行配置了,如:
module.exports = {
entry: {
catalog: {
import: './catalog.js',
},
},
};
此外,也可以定義輸出的文件名,之前都是通過 output.filename
進(jìn)行定義的:
module.exports = {
entry: {
about: { import: './about.js', filename: 'pages/[name][ext]' },
},
};
另外,入口文件的配置,新增了文件依賴定義、生成類庫的格式類型(commonjs 或 amd),也可以設(shè)置運(yùn)行時(shí)的名字,以及代碼塊加載的方式,更多細(xì)節(jié)可以參考完整的發(fā)布記錄。
排序與 ID
webpack 曾經(jīng)在編譯階段以特定的方式對模塊和代碼塊進(jìn)行排序,以遞增的方式分配 ID?,F(xiàn)在不再是這樣了。順序?qū)⒉辉儆糜?ID 的生成,取而代之的是,ID 生成的完全控制在插件中。優(yōu)化模塊和代碼塊順序的鉤子已經(jīng)被移除。
遷移:在編譯階段,你不能再依賴模塊和代碼塊的順序了。
從數(shù)組到集合(Set)
- Compilation.modules 現(xiàn)在是一個(gè)集合
- Compilation.chunks 現(xiàn)在是一個(gè)集合
- Chunk.files 現(xiàn)在是一個(gè)集合
存在一個(gè)適配層但會打印廢棄的警告。
遷移: 使用集合方法代替數(shù)組方法。
文件系統(tǒng)與信息變更
webpack 5 中,一個(gè)是需要使用 Compilation.fileSystemInfo
替代file/contextTimestamps
,獲取文件的時(shí)間戳信息,另一個(gè)是新增Compiler.modifiedFiles
以便更容易引用更改后的文件。
另外,還新增了一個(gè)類似于 compiler.inputFileSystem
和 compiler.outputFileSystem
的新 API compiler.intermediateFileSystem
,用于所有不被認(rèn)為是輸入或輸出的 fs 操作,如寫入 records,緩存或輸出 profiling。
模塊熱替換
HMR 運(yùn)行時(shí)已被重構(gòu)為運(yùn)行時(shí)模塊。HotUpdateChunkTemplate
已被合并入 ChunkTemplate
中。ChunkTemplates 和 plugins 也應(yīng)處理 HotUpdateChunk
了。
HMR 運(yùn)行時(shí)的 javascript 部分已從核心 HMR 運(yùn)行時(shí)鐘分離了出來。其他模塊類型現(xiàn)在也可以使用它們自己的方式處理 HMR。在未來,這將使得 HMR 處理諸如 mini-css-extract-plugin 或 WASM 模塊。
遷移:此為新功能,無需遷移。
import.meta.webpackHot
公開了與 module.hot
相同的 API。當(dāng)然可以在 ESM 模塊(.mjs,package.json 中的 type: "module")中使用,這些模塊不能訪問 module
。
工作隊(duì)列
webpack 曾經(jīng)通過函數(shù)調(diào)用函數(shù)的形式來進(jìn)行模塊處理,還有一個(gè) semaphore
選項(xiàng)限制并行性。Compilation.semaphore
已被移除,現(xiàn)在可以使用異步隊(duì)列處理,每個(gè)步驟都有獨(dú)立的隊(duì)列:
Compilation.factorizeQueue
:為一組 dependencies 調(diào)用模塊工廠。Compilation.addModuleQueue
:將模塊添加到編譯隊(duì)列中(可以使用緩存恢復(fù)模塊)Compilation.buildQueue
:必要時(shí)構(gòu)建模塊(可將模塊存儲到緩存中)Compilation.rebuildQueue
:如需手動觸發(fā),則會重新構(gòu)建模塊Compilation.processDependenciesQueue
:處理模塊的 dependencies。
這些隊(duì)列會有一些 hook 來監(jiān)聽并攔截工作的進(jìn)程。未來,多個(gè)編譯器會同時(shí)工作,可以通過攔截這些隊(duì)列來進(jìn)行編譯工作的編排。
遷移:此為新功能,無需遷移。
模塊和 chunk 圖
webpack 曾經(jīng)在依賴關(guān)系中存儲了已解析的模塊,并在 chunk 中存儲引入的模塊。但現(xiàn)已發(fā)生變化。所有關(guān)于模塊在模塊圖中如何連接的信息,現(xiàn)在都存儲在 ModulGraph 的 class 中。所有關(guān)于模塊與 chunk 如何連接的信息現(xiàn)在都已存儲在 ChunkGraph 的 class 中。依賴于 chunk 圖的信息也存儲在相關(guān)的 class 中。
以下列舉一些模塊的信息已被移動的例子:
- Module connections -> ModuleGraph
- Module issuer -> ModuleGraph
- Module optimization bailout -> ModuleGraph (TODO: check if it should ChunkGraph instead)
當(dāng)從緩存中恢復(fù)模塊時(shí),webpack 會將模塊從圖中斷開?,F(xiàn)在已無需這么做。一個(gè)模塊不存儲圖形的任何信息,技術(shù)上可以在多個(gè)圖形中使用。這會使得緩存變得更加容易。這部分變化中大多數(shù)都有一個(gè)適配層,當(dāng)使用時(shí),它會打印一個(gè)棄用警告。
遷移:在 ModuleGraph 和 ChunkGraph 上使用新的 API。
模塊 Source Types
Modules 現(xiàn)在必須通過 Module.getSourceTypes()
來定義它們支持的源碼類型。根據(jù)這一點(diǎn),不同的插件會用這些類型調(diào)用 source()
。對于源類型為 javascript
的JavascriptModulesPlugin
會將源代碼嵌入到 bundle 中。源類型 webassembly
的 WebAssemblyModulesPlugin
會 emit 一個(gè) wasm 文件。同時(shí),也支持自定義源類型,例如,mini-css-extract-plugin 會使用源類型為 stylesheet
將源碼嵌入到 css 文件中。
模塊類型與源類型間沒有關(guān)系。即使模塊類型為 json
,也可以使用源類型為 javascript
和模塊類型為 webassembly/experimental
的 javascript
和 webassembly
。
遷移:自定義模塊需要實(shí)現(xiàn)這些新的接口方法。
全新的觀察者
webpack 所使用的觀察者已重構(gòu)。它之前使用的是 chokidar
和原生依賴 fsevents
(僅在 OSX 上)?,F(xiàn)在它在只基于原生的 Node.js 中的 fs
。這意味著在 webpack 中已經(jīng)沒有原生依賴了。
它還能在監(jiān)聽時(shí)捕捉更多關(guān)于文件系統(tǒng)的信息。目前,它還可以捕獲 mtimes 和監(jiān)視事件時(shí)間,以及丟失文件的信息。為此,WatchFileSystem
API 做了一點(diǎn)小改動。在修改的同時(shí),我們還將 Arrays 轉(zhuǎn)換為 Sets,Objects 轉(zhuǎn)換為 Maps。
SizeOnlySource after emit
webpack 現(xiàn)在使用 SizeOnlySource
替換 Compilation.assets
中的 Sources,以減少內(nèi)存占用。
ExportsInfo
重構(gòu)了模塊導(dǎo)出信息的存儲方式。ModuleGraph 現(xiàn)在為每個(gè) Module
提供了一個(gè) ExportsInfo
,它用于存儲每個(gè) export 的信息。如果模塊僅以副作用的方式使用,它還存儲了關(guān)于未知 export 的信息,
對于每個(gè) export,都會存儲以下信息:
- 是否使用 export? 是否使用并不確定。(詳見
optimization.usedExports
)
- 是否提供 export? 是否提供并不確定。(詳見
optimization.providedExports
)
- 能否重命名 export 名? 是否重命名,也不確定
- 如果 export 已重新命名,則為新名稱。(詳見
optimization.mangleExports
)
- 嵌套的 ExportsInfo,如果 export 是一個(gè)含有附加信息的對象,那么它本身就是一個(gè)對象
-
- 用于重新導(dǎo)出命名空間對象:
import * as X from "..."; export { X };
- 用于表示 JSON 模塊中的結(jié)構(gòu)
- 用于重新導(dǎo)出命名空間對象:
代碼生成階段
編譯的代碼生成功能作為單獨(dú)的編譯階段。它不再隱藏在 Module.source()
和 Module.getRuntimeRequirements()
中運(yùn)行了。這應(yīng)該會使得流程更加簡潔。它還運(yùn)行報(bào)告該階段的進(jìn)度。并使得代碼生成在剖析時(shí)更加清晰可見。
遷移:Module.source()
和 Module.getRuntimeRequirements()
已棄用。使用 Module.codeGeneration()
代替。
依賴關(guān)系參考
webpack 曾經(jīng)有一個(gè)單一的方法和類型來表示依賴關(guān)系的引用(Compilation.getDependencyReference
會返回一個(gè) DependencyReference
)該類型用于引入關(guān)于該引用的所有信息,如 被引用的模塊,已經(jīng)引入了哪些 export,如果是弱引用,還需要訂閱一些相關(guān)信息。把所有這些信息構(gòu)建在一起,拿到參考的成本就很高,而且很頻繁(每次有人需要一個(gè)信息)。
在 webpack5 中,這部分代碼庫被重構(gòu)了,方法進(jìn)行了拆分。
- 引用的模塊可以從 ModuleGraphConnection 中讀取
- 引入的導(dǎo)出名,可以通過
Dependency.getReferencedExports()
獲取 Dependency
的 class 上會有一個(gè)weak
的 flag- 排序只與
HarmonyImportDependencies
相關(guān),可以通過sourceOrder
屬性獲取
Presentational Dependencies
這是 NormalModules
的一種新 Dependencies 類型:Presentational Dependencies
這些 dependencies 只在代碼生成階段使用,但在模塊圖構(gòu)建過程中未使用。所以它們永遠(yuǎn)不能引用模塊或影響導(dǎo)出/導(dǎo)入。
這些依賴關(guān)系的處理成本較低,webpack 會盡可能地使用它們
棄用 loaders
null-loader
已被棄用。使用
module.exports = {
resolve: {
alias: {
xyz$: false,
},
},
};
或者使用絕對路徑
module.exports = {
resolve: {
alias: {
[path.resolve(__dirname, '....')]: false,
},
},
};
總結(jié)
webpack 5 的大部分工作圍繞優(yōu)化展開,去除了 4 中有廢棄的內(nèi)容,新增了長期緩存,優(yōu)化了內(nèi)核等。本文只是挑重點(diǎn)為大家說明,具體變更請大家參考官方文檔。
官網(wǎng):https://webpack.js.org 中文文檔:https://webapck.docschina.org
以上就是W3Cschool編程獅
關(guān)于闊別兩年,webpack 5 正式發(fā)布了!的相關(guān)介紹了,希望對大家有所幫助。