Webpack 4 SplitChunksPlugin配置方案(轉)


通常情況下我們的 WebApp 是有我們的自身代碼和第三方庫組成的,我們自身的代碼是會常常變動的,而第三方庫除非有較大的版本升級,不然是不會變的,所以第三方庫和我們的代碼需要分開打包,我們可以給第三方庫設置一個較長的強緩存時間,這樣就不會頻繁請求第三方庫的代碼了。

那么如何提取第三方庫呢?在 webpack4.x 中, SplitChunksPlugin 插件取代了 CommonsChunkPlugin 插件來進行公共模塊抽取,我們可以對SplitChunksPlugin 進行配置進行 拆包 操作。

SplitChunksPlugin配置示意如下:

optimization: {
    splitChunks: { chunks: "initial", // 代碼塊類型 必須三選一: "initial"(初始化) | "all"(默認就是all) | "async"(動態加載) minSize: 0, // 最小尺寸,默認0 minChunks: 1, // 最小 chunk ,默認1 maxAsyncRequests: 1, // 最大異步請求數, 默認1 maxInitialRequests: 1, // 最大初始化請求書,默認1 name: () => {}, // 名稱,此選項課接收 function cacheGroups: { // 緩存組會繼承splitChunks的配置,但是test、priorty和reuseExistingChunk只能用於配置緩存組。 priority: "0", // 緩存組優先級,即權重 false | object | vendor: { // key 為entry中定義的 入口名稱 chunks: "initial", // 必須三選一: "initial"(初始化) | "all" | "async"(默認就是異步) test: /react|lodash/, // 正則規則驗證,如果符合就提取 chunk name: "vendor", // 要緩存的 分隔出來的 chunk 名稱 minSize: 0, minChunks: 1, enforce: true, reuseExistingChunk: true // 可設置是否重用已用chunk 不再創建新的chunk } } } } 復制代碼

SplitChunksPlugin 的配置項很多,可以先去官網了解如何配置,我們現在只簡單列舉了一下配置元素。

如果我們想抽取第三方庫可以這樣簡單配置

   splitChunks: {
      chunks: 'all', // initial、async和all minSize: 30000, // 形成一個新代碼塊最小的體積 maxAsyncRequests: 5, // 按需加載時候最大的並行請求數 maxInitialRequests: 3, // 最大初始化請求數 automaticNameDelimiter: '~', // 打包分割符 name: true, cacheGroups: { vendor: { name: "vendor", test: /[\\/]node_modules[\\/]/, //打包第三方庫 chunks: "all", priority: 10 // 優先級 }, common: { // 打包其余的的公共代碼 minChunks: 2, // 引入兩次及以上被打包 name: 'common', // 分離包的名字 chunks: 'all', priority: 5 }, } }, 復制代碼

這樣似乎大功告成了?並沒有,我們的配置有很大的問題:

  1. 我們粗暴得將第三方庫一起打包可行嗎? 當然是有問題的,因為將第三方庫一塊打包,只要有一個庫我們升級或者引入一個新庫,這個 chunk 就會變動,那么這個chunk 的變動性會很高,並不適合長期緩存,還有一點,我們要提高首頁加載速度,第一要務是減少首頁加載依賴的代碼量,請問像 react vue reudx 這種整個應用的基礎庫我們是首頁必須要依賴的之外,像 d3.js three.js這種特定頁面才會出現的特殊庫是沒必要在首屏加載的,所以我們需要將應用基礎庫和特定依賴的庫進行分離。
  2. 當 chunk 在強緩存期,但是服務器代碼已經變動了我們怎么通知客戶端?上面我們的示意圖已經看到了,當命中的資源在緩存期內,瀏覽器是直接讀取緩存而不會向服務器確認的,如果這個時候服務器代碼已經變動了,怎么辦?這個時候我們不能將 index.html 緩存(反正webpack時代的 html 頁面小到沒有緩存的必要),需要每次引入 script 腳本的時候去服務器更新,並開啟 hashchunk,它的作用是當 chunk 發生改變的時候會生成新的 hash 值,如果不變就不發生變動,這樣當 index 加載后續 script資源時如果 hashchunk 沒變就會命中緩存,如果改變了那么會重新去服務端加載新資源。

下圖示意了如何將第三方庫進行拆包,基礎型的 react 等庫與工具性的 lodash 和特定庫 Echarts 進行拆分

      cacheGroups: {
        reactBase: { name: 'reactBase', test: (module) => { return /react|redux/.test(module.context); }, chunks: 'initial', priority: 10, }, utilBase: { name: 'utilBase', test: (module) => { return /rxjs|lodash/.test(module.context); }, chunks: 'initial', priority: 9, }, uiBase: { name: 'chartBase', test: (module) => { return /echarts/.test(module.context); }, chunks: 'initial', priority: 8, }, commons: { name: 'common', chunks: 'initial', priority: 2, minChunks: 2, }, } 復制代碼

我們對 chunk 進行 hash 化,正如下圖所示,我們變動 chunk2 相關的代碼后,其它 chunk 都沒有變化,只有 chunk2 的 hash 改變了

  output: {
    filename: mode === 'production' ? '[name].[chunkhash:8].js' : '[name].js', chunkFilename: mode === 'production' ? '[id].[chunkhash:8].chunk.js' : '[id].js', path: getPath(config.outputPath) } 復制代碼

 

image.png image.png

 

我們通過 http 緩存+webpack hash 緩存策略使得前端項目充分利用了緩存的優勢,但是 webpack 之所以需要傳說中的 webpack配置工程師 是有原因的,因為 webpack 本身是玄學,還是以上圖為例,如果你 chunk2的相關代碼去除了一個依賴或者引入了新的但是已經存在工程中依賴,會怎么樣呢?

我們正常的期望是,只有 chunk2 發生變化了,但是事實上是大量不相干的 chunk 的 hash 發生了變動,這就導致我們緩存策略失效了,下圖是變更后的 hash,我們用紅圈圈起來的都是 hash 變動的,而事實上我們只變動了 chunk2 相關的代碼,為什么會這樣呢?

 

image.png 原因是 webpack 會給每個 chunk 搭上 id,這個 id 是自增的,比如 chunk 0 中的id 為 0,一旦我們引入新的依賴,chunk 的自增會被打亂,這個時候又因為 hashchunk 根據內容生成 hash,這就導致了 id 的變動致使 hashchunk 發生巨變,雖然代碼內容根本沒有變化。 image.png
這個問題我們需要額外引入一個插件HashedModuleIdsPlugin,他用非自增的方式進行 chunk id 的命名,可以解決這個問題,雖然 webpack 號稱 0 配置了,但是這個常用功能沒有內置,要等到下個版本了。
image.png

 

webpack hash緩存相關內容建議閱讀此文章 作為拓展


作者:尋找海藍96
鏈接:https://juejin.im/post/5d00820b5188255ee806a1c7
來源:掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM