原文連接前端
繼 webpack v5-beta0 發佈後,官方又發佈了持久化緩存指南。node
首先,要注意的是默認狀況下不會啓用持久化緩存。你能夠自行選擇啓用。webpack
爲什麼如此? webpack 旨在注重構建安全而非性能。 咱們沒有打算默認啓用這一功能,主要緣由在於此功能雖然有 95% 概率提高性能,但仍有 5% 的概率中斷你的應用程序/工做流/構建。web
這可能聽起來很糟,但相信我它並不是如此。 只不過須要開發人員進行額外的操做來配置它。typescript
序列化與反序列化功能具備無需配置的開箱即用體驗,但開箱即用的部分可能導致緩存失效。數據庫
什麼是緩存失效? webpack 須要確認 entry 的緩存什麼時候會失效,並在失效時再也不將其用於構建。 所以,當你應用程序修改文件時,就會發生此狀況。npm
示例:修改 magic.js
。 webpack 必須讓 entry 爲 magic.js
的緩存失效。 構建將從新處理該文件,即運行 babel,typescript 諸如此類工具,從新解析文件並運行代碼生成。 webpack 可能還會導致 entry 爲 bundle.js
的緩存失效。 而後根據原模塊從新構建此文件。json
爲此,webpack 追蹤了每一個模塊的 fileDependencies
contextDependencies
以及 missingDependencies
,並建立了文件系統快照。 此快照會與真實文件系統進行比較,當檢測到差別時,將觸發對應模塊的從新構建。緩存
webpack 給 bundle.js
的緩存 entry 設置了一個 etag
,它爲全部貢獻者的 hash 值。 比較這個 etag
,只有當它與緩存 entry 匹配時才能使用。安全
webpack 4 中的內存緩存也依賴上述這些。 從開發人員角度來講,這些都可以開箱即用,無需額外配置。 但對於 webpack 5 的持久化緩存來講,卻充滿着挑戰。
如下操做均會讓 webpack 使 entry 緩存失效:
這變得很是棘手。 開箱即用的狀況下,webpack 沒法處理全部這些狀況。 這就是咱們爲何選擇安全的方式,並將持久化緩存變爲可選特性的緣由。 咱們但願讀者能夠學習如何啓用持久化緩存,覺得你提供正確的提示。 咱們但願你知道須要使用哪一種配置來處理你自定義的構建腳本。
爲了處理構建過程當中的依賴關係,webpack 提供了三個新工具:
此爲全新的配置項 cache.buildDependencies
,它能夠指定構建過程當中的代碼依賴。 爲了使它更簡易,webpack 負責解析並遵循配置值的依賴。
值類型有兩種:文件和目錄。 目錄類型必須以斜槓(/
)結尾。其餘全部內容都解析爲文件類型。
對於目錄類型來講,會解析其最近的 package.json
中的 dependencies。 對於文件類型來講,咱們將查看 node.js 模塊緩存以尋找其依賴。
示例:構建一般取決於 webpack 自己的 lib 文件夾: 你能夠這樣配置:
cache.buildDependencies: {
defaultWebpack: ["webpack/lib/"]
}
複製代碼
當 webpack/lib
或 webpack 依賴的庫(如,watchpack
,enhanced-resolved
等)發生任何變化時,其緩存將失效。 webpack/lib
已經是默認值,默認狀況下無需配置。
另外一個示例:構建依舊取決於你的配置文件。 具體配置以下:
cache.buildDependencies: {
config: [__filename]
}
複製代碼
__filename
變量指向 node.js 中的當前文件。
當配置文件或配置文件中經過 require
依賴的任何內容發生更改時,也會使得持久化緩存失效。 當配置文件經過 require()
引用了全部使用過的插件時,它們也會成爲構建依賴項。
若是配置文件經過 fs.readFile
讀取文件,則將不會成爲構建依賴項,由於 webpack 僅遵循 require()
。 你須要手動將此類文件添加到 buildDependencies
中。
構建的某些依賴項不能單純的依靠對文件的引用,如,從數據庫讀取的值,環境變量或命令行上傳遞的值。 對於這些值,咱們給出了新的配置項 cache.version
。
cache.version
類型爲 string。傳遞不一樣的字符串將使持久化緩存失效。
示例:你的配置中可能會讀取環境變量中的 GIT_REV
並將其與 DefinePlugin
一塊兒使用以將其嵌入到 bundle 中。 這使得 GIT_REV
成爲你構建的依賴項。 具體配置以下:
cache: {
version: `${process.env.GIT_REV}`
}
複製代碼
在某些狀況下,依賴關係會在多個不一樣的值間切換,而且對於每一個值更改都會使得持久化緩存失效,這顯然是浪費資源的。 對於這類值,咱們給出了新的配置項 cache.name
。
cache.name
類型爲 string。傳遞值將建立一個隔離且獨立的持久化緩存。
cache.name
被用於對文件名進行持久化緩存。確保僅傳遞短小且 fs-safe 的名稱。
示例:你的配置可使用 --env.target mobile|desktop
參數爲移動端或 PC 用戶建立不一樣的構建。 具體配置以下:
cache: {
name: `${env.target}`
}
複製代碼
對大部分 node_modules 進行哈希處理並加蓋時間戳以生存構建和常規依賴項,其代價很是昂貴,而且還會大大下降 webpack 的執行速度。 爲避免這種狀況出現,webpack 引入了相關的性能優化,默認狀況下會跳過 node_modules
,並使用 package.json
中的 version
和 name
做爲數據源。
此優化將用於配置項 cache.managedPaths
中的全部 path。 它默認爲 webpack 安裝了 node_modules
目錄。
啓用此優化後,請勿手動編輯 node_modules
。 你可使用 cache.managedPaths: []
禁用它。
當使用 Yarn PnP 時,將啓用另外一個優化。 因爲緩存內容不可變,yarn 緩存中的全部文件都將徹底跳過哈希和時間戳的操做(甚至不會追蹤 version
和 name
)。
此操做由配置項 cache.immutablePaths
控制。 啓用 Yarn PnP 時,默認爲安裝了 webpack 的 yarn 緩存。
不要手動編輯 yarn 緩存,由於這根本不可行。
確保你已閱讀並理解以上信息!
此爲啓用持久化緩存的典型配置:
cache: {
type: "filesystem",
buildDependencies: {
config: [ __filename ] // 當你 CLI 自動添加它時,你能夠忽略它
}
}
複製代碼
持久化緩存可用於單獨構建和連續構建(watch)。
當設置 cache.type: "filesystem"
時,webpack 會在內部以分層方式啓用文件系統緩存和內存緩存。 從緩存讀取時,會先查看內存緩存,若是內存緩存未找到,則降級到文件系統緩存。 寫入緩存將同時寫入內存緩存和文件系統緩存。
文件系統緩存不會直接將對磁盤寫入的請求進行序列化。它將等到編譯過程完成且編譯器處於空閒狀態纔會執行。 如此處理的緣由是序列化和磁盤寫入會佔用資源,而且咱們不想額外延遲編譯過程。
針對單一構建,其工做流爲:
針對連續構建(watch),其工做流爲:
cache.idleTimeoutForInitialStore
cache.idleTimeout
你會發現兩個新的配置項 cache.idleTimeout
和 cache.idleTimeoutForInitialStore
,它們控制着持久化緩存以前編譯器必須空閒的時長。 cache.idleTimeout
默認爲 60s,cache.idleTimeoutForInitialStore
默認爲 0s。 因爲序列化阻止了事件循環,所以在序列化緩存時不進行緩存檢測。 此延遲嘗試避免因爲快速編輯文件,而在 watch 模式下致使從新編譯形成的延遲,同時嘗試爲下一次冷啓動保持持久化緩存的最新狀態。 這是一個折中的解決方案,能夠設置適合你工做流的值。較小的值會縮短冷啓動時間,但會增長延遲從新構建的風險。
發生錯誤要恢復持久化緩存的方式,能夠經過刪除整個緩存並進行全新的構建,或者經過刪除有問題的緩存 entry 並使得該項目保持未緩存狀態來進行。
在這種狀況下,webpack 的 logger 會發出警告。 欲瞭解更多,請參閱 infrastructureLogging
的配置項。
正常使用不須要如下信息。
封裝 webpack 的工具能夠選擇其餘默認值。 當不容許使用自定義擴展的 webpack 時,因爲能夠徹底控制全部構建的依賴項,所以能夠默認打開持久化存儲。
默認狀況下,使用 webpack 的 CLI 可能會添加一些構建依賴關係,而 webpack 自己不會。
cache.buildDependencies.defaultConfig
設置爲所用的配置文件cache.version
cache.name
中添加註釋。使用以下配置,將輸出額外的調試信息:
infrastructureLogging: {
debug: /webpack\.cache/
}
複製代碼
version
與 cache.version
不匹配 -> 沒有構建緩存resolve snapshot
)與文件系統進行對比
resolve results
)
build dependencies snapshot
)與文件系統進行對比
cache.buildDependencies
全部支持序列化的 class 都須要註冊一個序列化器:
webpack.util.serialization.register(Constructor, request, name, serializer);
複製代碼
Constructor
應爲一個 class 或構造器函數。 對於任何須要序列化的對象的 object.constructor
將被用於查找序列化器(serializer)。
request
將被用於加載調用 register
模塊。 它應指向當前模塊。 它將以這種方式使用:require(request)
。
name
被用於區分具備相同 request
的多個 register
調用。
serializer
是至少擁有 serialize
和 deserialize
兩個方法的對象。
當需序列化對象時,請調用 serializer.serialize(object, context)
。 context
是至少擁有一個 write(anything)
方法的對象 此方法將內容寫入輸出流。 傳遞的值也會被序列化。
當須要反序列化對象時,請調用 serializer.deserialize(context)
。 context
是至少擁有一個 read(): anything
方法的對象。 此方法會反序列化輸入流中的某些內容。 deserialize
必須返回反序列化後的對象。
serialize
和 deserialize
應以相同的順序讀取和寫入相同的對象。
示例:
// some-module/lib/MyClass.js
class MyClass {
constructor(a, b) {
this.a = a;
this.b = b;
this.c = undefined;
}
}
register(MyClass, "some-module/lib/MyClass", null, {
seralize(obj, { write }) {
write(obj.a);
write(obj.b);
write(obj.c);
}
deserialize({ read }) {
const obj = new MyClass(read(), read());
obj.c = read();
return obj;
}
});
複製代碼
基本數據類型和引用數據類型的序列化器都已被註冊,即 string,number,Array,Set,Map,RegExp,plain objects,Error。
如對譯文有疑問,歡迎評論。
相關文章會在公衆號首發,掃描下方二維碼關注咱們,咱們將提供前端相關最新最優含量的資訊。