SplitChunksPlugin

原本,區塊(以及在其中匯入的模組)透過內部 webpack 圖表中的父子關係連接。CommonsChunkPlugin 用於避免它們之間重複的依賴關係,但無法進行進一步的最佳化。

自 webpack v4 開始,CommonsChunkPlugin 已被移除,改用 optimization.splitChunks

預設值

開箱即用的 SplitChunksPlugin 應能順利運作於大多數使用者。

預設值僅影響依需求區塊,因為變更初始區塊會影響 HTML 檔案應包含的腳本標籤,以執行專案。

Webpack 會根據下列條件自動分割區塊

  • 新區塊可以共用,或模組來自 node_modules 資料夾
  • 新區塊會大於 20kb(在 min+gz 之前)
  • 依需求載入區塊時,平行要求的最大數量會低於或等於 30
  • 初始頁面載入時,平行要求的最大數量會低於或等於 30

在嘗試滿足最後兩個條件時,較大的區塊會優先。

組態

Webpack 提供一組選項,讓開發人員可以更進一步控制此功能。

optimization.splitChunks

此組態物件表示 SplitChunksPlugin 的預設行為。

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'async',
      minSize: 20000,
      minRemainingSize: 0,
      minChunks: 1,
      maxAsyncRequests: 30,
      maxInitialRequests: 30,
      enforceSizeThreshold: 50000,
      cacheGroups: {
        defaultVendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          reuseExistingChunk: true,
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },
};

splitChunks.automaticNameDelimiter

字串 = '~'

預設情況下,webpack 會使用區塊的來源和名稱來產生名稱(例如 vendors~main.js)。此選項讓您可以指定用於產生名稱的分隔符號。

splitChunks.chunks

字串 = 'async' 函式 (chunk) 正規表示式

這表示將選取哪些區塊進行最佳化。當提供字串時,有效值為 allasyncinitial。提供 all 可能特別有效,因為這表示區塊甚至可以在非同步區塊之間共用。

請注意,它也套用於後備快取群組 (splitChunks.fallbackCacheGroup.chunks)。

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      // include all types of chunks
      chunks: 'all',
    },
  },
};

或者,您可以提供一個函式以取得更多控制權。傳回值將表示是否包含每個區塊。

module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks(chunk) {
        // exclude `my-excluded-chunk`
        return chunk.name !== 'my-excluded-chunk';
      },
    },
  },
};

如果您使用的是 webpack 版本 5.86.0 或更新版本,您也可以傳遞正規表示法

module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: /foo/,
    },
  },
};

splitChunks.maxAsyncRequests

數字 = 30

隨選載入時的並行要求最大數目。

splitChunks.maxInitialRequests

數字 = 30

進入點的並行要求最大數目。

splitChunks.defaultSizeTypes

[字串] = ['javascript', 'unknown']

當數字用於大小時,設定所使用的類型大小。

splitChunks.minChunks

數字 = 1

在分割之前,模組必須在區塊之間共用的最少次數。

splitChunks.hidePathInfo

布林值

在為由 maxSize 分割的部分建立名稱時,防止公開路徑資訊。

splitChunks.minSize

數字 = 20000 { [索引: 字串]: 數字 }

要產生區塊的最小位元組大小。

splitChunks.minSizeReduction

數字 { [index: 字串]: 數字 }

要產生區塊,主區塊 (套件) 的最小大小縮減,以位元組為單位。表示如果將主區塊 (套件) 的大小縮減的位元組數小於指定數量,即使符合 splitChunks.minSize 值,也不會分割,也不會產生區塊。

splitChunks.enforceSizeThreshold

splitChunks.cacheGroups.{cacheGroup}.enforceSizeThreshold

數字 = 50000

強制分割的大小臨界值,並忽略其他限制 (minRemainingSize、maxAsyncRequests、maxInitialRequests)。

splitChunks.minRemainingSize

splitChunks.cacheGroups.{cacheGroup}.minRemainingSize

數字 = 0

splitChunks.minRemainingSize 選項在 webpack 5 中引入,用於避免零大小模組,方法是確保分割後剩餘區塊的最小大小高於限制。在 「開發」模式 中預設為 0。在其他情況下,splitChunks.minRemainingSize 預設為 splitChunks.minSize 的值,因此不需要手動指定,除非需要深入控制的罕見情況。

splitChunks.layer

splitChunks.cacheGroups.{cacheGroup}.layer

RegExp 字串 函式

依據模組層級將模組指定至快取群組。

splitChunks.maxSize

數字 = 0

使用 maxSize(全域 optimization.splitChunks.maxSize 每個快取群組 optimization.splitChunks.cacheGroups[x].maxSize 或預設快取群組 optimization.splitChunks.fallbackCacheGroup.maxSize)告知 webpack 嘗試將大於 maxSize 位元組的區塊分割成較小的部分。部分將至少為 minSize(緊接在 maxSize 之後)的大小。演算法具有確定性,而模組的變更僅會產生區域影響。因此,在使用長期快取時可用,且不需要記錄。maxSize 僅為提示,且當模組大於 maxSize 或分割會違反 minSize 時可能會遭到違反。

當區塊已具有名稱時,每個部分都會從該名稱衍生出一個新名稱。根據 optimization.splitChunks.hidePathInfo 的值,它會新增一個衍生自第一個模組名稱或其雜湊的鍵。

maxSize 選項旨在與 HTTP/2 和長期快取搭配使用。它會增加要求次數以改善快取。它也可以用於縮小檔案大小以加快重建速度。

splitChunks.maxAsyncSize

數字

maxSize 相同,maxAsyncSize 可以套用於全域 (splitChunks.maxAsyncSize)、快取群組 (splitChunks.cacheGroups.{cacheGroup}.maxAsyncSize) 或備用快取群組 (splitChunks.fallbackCacheGroup.maxAsyncSize)。

maxAsyncSizemaxSize 的差異在於 maxAsyncSize 僅會影響依需求載入的區塊。

splitChunks.maxInitialSize

數字

maxSize 相同,maxInitialSize 可以套用於全域 (splitChunks.maxInitialSize)、快取群組 (splitChunks.cacheGroups.{cacheGroup}.maxInitialSize) 或備用快取群組 (splitChunks.fallbackCacheGroup.maxInitialSize)。

maxInitialSizemaxSize 的差異在於 maxInitialSize 僅會影響初始載入的區塊。

splitChunks.name

boolean = false function (module, chunks, cacheGroupKey) => string string

每個快取群組也可用:splitChunks.cacheGroups.{cacheGroup}.name

分割區塊的名稱。提供 false 會保留區塊的相同名稱,因此不會不必要地變更名稱。這是建議用於生產建置的值。

提供字串或函式可讓您使用自訂名稱。指定字串或總是傳回相同字串的函式會將所有常見模組和廠商合併至單一區塊。這可能會導致較大的初始下載量並降低頁面載入速度。

如果您選擇指定函式,您可能會發現 chunk.name 屬性(其中 chunkchunks 陣列的元素)在為區塊選擇名稱時特別有用。

如果 splitChunks.name進入點名稱相符,進入點將會移除。

main.js

import _ from 'lodash';

console.log(_.join(['Hello', 'webpack'], ' '));

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        commons: {
          test: /[\\/]node_modules[\\/]/,
          // cacheGroupKey here is `commons` as the key of the cacheGroup
          name(module, chunks, cacheGroupKey) {
            const moduleFileName = module
              .identifier()
              .split('/')
              .reduceRight((item) => item);
            const allChunksNames = chunks.map((item) => item.name).join('~');
            return `${cacheGroupKey}-${allChunksNames}-${moduleFileName}`;
          },
          chunks: 'all',
        },
      },
    },
  },
};

使用下列 splitChunks 設定執行 webpack 也會輸出與下一個名稱共用的群組區塊:commons-main-lodash.js.e7519d2bb8777058fa27.js(雜湊作為實際世界輸出的範例)。

splitChunks.usedExports

splitChunks.cacheGroups{cacheGroup}.usedExports

布林值 = true

找出模組使用哪些輸出,以扭曲輸出名稱、略過未使用的輸出,並產生更有效率的程式碼。當其為 true 時:分析每個執行時間使用的輸出,當其為 "global" 時:分析所有執行時間合併的輸出。

splitChunks.cacheGroups

快取群組可以繼承和/或覆寫 splitChunks.* 中的任何選項;但 testpriorityreuseExistingChunk 只能在快取群組層級中設定。若要停用任何預設快取群組,請將它們設為 false

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        default: false,
      },
    },
  },
};

splitChunks.cacheGroups.{cacheGroup}.priority

number = -20

模組可以屬於多個快取群組。最佳化會偏好優先度較高的快取群組。預設群組具有負優先度,以允許自訂群組取得較高的優先度(自訂群組的預設值為 0)。

splitChunks.cacheGroups.{cacheGroup}.reuseExistingChunk

布林值 = true

如果目前的區塊包含已從主套件分割出來的模組,它會被重複使用,而不是產生新的區塊。這可能會影響區塊的結果檔名。

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        defaultVendors: {
          reuseExistingChunk: true,
        },
      },
    },
  },
};

splitChunks.cacheGroups.{cacheGroup}.type

function RegExp string

允許依據模組類型將模組指定給快取群組。

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        json: {
          type: 'json',
        },
      },
    },
  },
};

splitChunks.cacheGroups.test

splitChunks.cacheGroups.{cacheGroup}.test

function (module, { chunkGraph, moduleGraph }) => boolean RegExp string

控制由這個快取群組選取的模組。省略它會選取所有模組。它可以符合絕對模組資源路徑或區塊名稱。當符合區塊名稱時,區塊中的所有模組都會被選取。

提供一個函式給 {cacheGroup}.test

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        svgGroup: {
          test(module) {
            // `module.resource` contains the absolute path of the file on disk.
            // Note the usage of `path.sep` instead of / or \, for cross-platform compatibility.
            const path = require('path');
            return (
              module.resource &&
              module.resource.endsWith('.svg') &&
              module.resource.includes(`${path.sep}cacheable_svgs${path.sep}`)
            );
          },
        },
        byModuleTypeGroup: {
          test(module) {
            return module.type === 'javascript/auto';
          },
        },
      },
    },
  },
};

為了查看 modulechunks 物件中有哪些資訊可用,您可以在 callback 中放置 debugger; 陳述式。然後 以偵錯模式執行您的 webpack 建置 以在 Chromium DevTools 中檢查參數。

提供一個 RegExp{cacheGroup}.test

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        defaultVendors: {
          // Note the usage of `[\\/]` as a path separator for cross-platform compatibility.
          test: /[\\/]node_modules[\\/]|vendor[\\/]analytics_provider|vendor[\\/]other_lib/,
        },
      },
    },
  },
};

splitChunks.cacheGroups.{cacheGroup}.filename

字串 函式 (pathData, assetInfo) => 字串

允許在初次分割時覆寫檔名。所有在 output.filename 中可用的佔位符在此處也可用。

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        defaultVendors: {
          filename: '[name].bundle.js',
        },
      },
    },
  },
};

作為函式

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        defaultVendors: {
          filename: (pathData) => {
            // Use pathData object for generating filename string based on your requirements
            return `${pathData.chunk.name}-bundle.js`;
          },
        },
      },
    },
  },
};

透過提供路徑作為檔名前綴,可以建立資料夾結構:'js/vendor/bundle.js'

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        defaultVendors: {
          filename: 'js/[name]/bundle.js',
        },
      },
    },
  },
};

splitChunks.cacheGroups.{cacheGroup}.enforce

布林值 = false

指示 webpack 忽略 splitChunks.minSizesplitChunks.minChunkssplitChunks.maxAsyncRequestssplitChunks.maxInitialRequests 選項,並永遠為此快取群組建立分割區塊。

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        defaultVendors: {
          enforce: true,
        },
      },
    },
  },
};

splitChunks.cacheGroups.{cacheGroup}.idHint

字串

設定分割區塊 ID 的提示。它會新增到分割區塊的檔名中。

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        defaultVendors: {
          idHint: 'vendors',
        },
      },
    },
  },
};

範例

預設值:範例 1

// index.js

import('./a'); // dynamic import
// a.js
import 'react';

//...

結果:會建立一個包含 react 的獨立分割區塊。在匯入呼叫時,此分割區塊會與包含 ./a 的原始分割區塊並行載入。

原因

  • 條件 1:區塊包含來自 node_modules 的模組
  • 條件 2:react 大於 30kb
  • 條件 3:匯入呼叫的並行要求數目為 2
  • 條件 4:不會影響初始頁面載入的要求

背後的理由是什麼?react 可能不會像應用程式程式碼那樣經常變更。透過將其移至一個獨立的區塊,此區塊可以與應用程式程式碼分開快取(假設您使用區塊雜湊、記錄、快取控制或其他長期快取方法)。

預設值:範例 2

// entry.js

// dynamic imports
import('./a');
import('./b');
// a.js
import './helpers'; // helpers is 40kb in size

//...
// b.js
import './helpers';
import './more-helpers'; // more-helpers is also 40kb in size

//...

結果:將會建立一個包含 ./helpers 及其所有相依項目的獨立區塊。在匯入呼叫時,此區塊會與原始區塊並行載入。

原因

  • 條件 1:區塊由兩個匯入呼叫共用
  • 條件 2:helpers 大於 30kb
  • 條件 3:匯入呼叫的並行要求數目為 2
  • 條件 4:不會影響初始頁面載入的要求

helpers 的內容放入每個區塊會導致其程式碼被下載兩次。透過使用獨立區塊,這只會發生一次。我們支付額外要求的成本,這可以視為權衡。這就是為什麼有 30kb 的最小大小。

分割區塊:範例 1

建立一個 commons 區塊,其中包含所有進入點之間共用的程式碼。

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        commons: {
          name: 'commons',
          chunks: 'initial',
          minChunks: 2,
        },
      },
    },
  },
};

分割區塊:範例 2

建立一個 vendors 區塊,其中包含整個應用程式中所有來自 node_modules 的程式碼。

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        commons: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
        },
      },
    },
  },
};

分割區塊:範例 3

建立一個 自訂供應商 區塊,其中包含由 RegExp 匹配的特定 node_modules 套件。

webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
          name: 'vendor',
          chunks: 'all',
        },
      },
    },
  },
};

17 貢獻者

sokrajeremenichelliPriestchchrisdothtmlEugeneHlushkobyzykjacobangelmadhavarshneysakhisheikhsuperburritoryandrew14snitin315chenxsanrohrlafjamesgeorge007anshumanvsnitin315