SplitChunksPlugin

2023-05-22 11:06 更新

最初,chunks(以及內(nèi)部導入的模塊)是通過內(nèi)部 webpack 圖譜中的父子關系關聯(lián)的。CommonsChunkPlugin 曾被用來避免他們之間的重復依賴,但是不可能再做進一步的優(yōu)化。

從 webpack v4 開始,移除了 CommonsChunkPlugin,取而代之的是 optimization.splitChunks。

默認值

開箱即用的 SplitChunksPlugin 對于大部分用戶來說非常友好。

默認情況下,它只會影響到按需加載的 chunks,因為修改 initial chunks 會影響到項目的 HTML 文件中的腳本標簽。

webpack 將根據(jù)以下條件自動拆分 chunks:

  • 新的 chunk 可以被共享,或者模塊來自于 node_modules 文件夾
  • 新的 chunk 體積大于 20kb(在進行 min+gz 之前的體積)
  • 當按需加載 chunks 時,并行請求的最大數(shù)量小于或等于 30
  • 當加載初始化頁面時,并發(fā)請求的最大數(shù)量小于或等于 30

當嘗試滿足最后兩個條件時,最好使用較大的 chunks。

配置

webpack 為希望對該功能進行更多控制的開發(fā)者提供了一組選項。

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

?string = '~'?

默認情況下,webpack 將使用 chunk 的來源和名稱生成名稱(例如 vendors~main.js)。此選項使你可以指定用于生成名稱的分隔符。

splitChunks.chunks

?string = 'async' function (chunk)?

這表明將選擇哪些 chunk 進行優(yōu)化。當提供一個字符串,有效值為 all,async 和 initial。設置為 all 可能特別強大,因為這意味著 chunk 可以在異步和非異步 chunk 之間共享。

請注意,它也應用于回退緩存組(splitChunks.fallbackCacheGroup.chunks).

webpack.config.js

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

或者,你也可以提供一個函數(shù)去做更多的控制。這個函數(shù)的返回值將決定是否包含每一個 chunk。

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

splitChunks.maxAsyncRequests

?number = 30?

按需加載時的最大并行請求數(shù)。

splitChunks.maxInitialRequests

?number = 30?

入口點的最大并行請求數(shù)。

splitChunks.defaultSizeTypes

[string] = ['javascript', 'unknown']

Sets the size types which are used when a number is used for sizes.

splitChunks.minChunks

?number = 1?

拆分前必須共享模塊的最小 chunks 數(shù)。

splitChunks.hidePathInfo

?boolean?

為由 maxSize 分割的部分創(chuàng)建名稱時,阻止公開路徑信息。

splitChunks.minSize

?number = 20000 { [index: string]: number }?

生成 chunk 的最小體積(以 bytes 為單位)。

splitChunks.minSizeReduction

?number { [index: string]: number }?

生成 chunk 所需的主 chunk(bundle)的最小體積(以字節(jié)為單位)縮減。這意味著如果分割成一個 chunk 并沒有減少主 chunk(bundle)的給定字節(jié)數(shù),它將不會被分割,即使它滿足 splitChunks.minSize。

splitChunks.enforceSizeThreshold

splitChunks.cacheGroups.{cacheGroup}.enforceSizeThreshold

?number = 50000?

強制執(zhí)行拆分的體積閾值和其他限制(minRemainingSize,maxAsyncRequests,maxInitialRequests)將被忽略。

splitChunks.minRemainingSize

splitChunks.cacheGroups.{cacheGroup}.minRemainingSize

?number = 0?

在 webpack 5 中引入了 splitChunks.minRemainingSize 選項,通過確保拆分后剩余的最小 chunk 體積超過限制來避免大小為零的模塊。 'development' 模式 中默認為 0。對于其他情況,splitChunks.minRemainingSize 默認為 splitChunks.minSize 的值,因此除需要深度控制的極少數(shù)情況外,不需要手動指定它。

splitChunks.layer

splitChunks.cacheGroups.{cacheGroup}.layer

?RegExp? ?string? ?function?

按模塊層將模塊分配給緩存組。

splitChunks.maxSize

?number = 0?

使用 maxSize(每個緩存組 optimization.splitChunks.cacheGroups[x].maxSize 全局使用 optimization.splitChunks.maxSize 或?qū)髠渚彺娼M optimization.splitChunks.fallbackCacheGroup.maxSize 使用)告訴 webpack 嘗試將大于 maxSize 個字節(jié)的 chunk 分割成較小的部分。 這些較小的部分在體積上至少為 minSize(僅次于 maxSize)。 該算法是確定性的,對模塊的更改只會產(chǎn)生局部影響。這樣,在使用長期緩存時就可以使用它并且不需要記錄。maxSize 只是一個提示,當模塊大于 maxSize 或者拆分不符合 minSize 時可能會被違反。

當 chunk 已經(jīng)有一個名稱時,每個部分將獲得一個從該名稱派生的新名稱。 根據(jù) optimization.splitChunks.hidePathInfo 的值,它將添加一個從第一個模塊名稱或其哈希值派生的密鑰。

maxSize 選項旨在與 HTTP/2 和長期緩存一起使用。它增加了請求數(shù)量以實現(xiàn)更好的緩存。它還可以用于減小文件大小,以加快二次構(gòu)建速度。

splitChunks.maxAsyncSize

?number?

像 maxSize 一樣,maxAsyncSize 可以為 cacheGroups(splitChunks.cacheGroups.{cacheGroup}.maxAsyncSize)或 fallback 緩存組(splitChunks.fallbackCacheGroup.maxAsyncSize )全局應用(splitChunks.maxAsyncSize)

maxAsyncSize 和 maxSize 的區(qū)別在于 maxAsyncSize 僅會影響按需加載 chunk。

splitChunks.maxInitialSize

?number?

像 maxSize 一樣,maxInitialSize 可以對 cacheGroups(splitChunks.cacheGroups.{cacheGroup}.maxInitialSize)或 fallback 緩存組(splitChunks.fallbackCacheGroup.maxInitialSize)全局應用(splitChunks.maxInitialSize)。

maxInitialSize 和 maxSize 的區(qū)別在于 maxInitialSize 僅會影響初始加載 chunks。

splitChunks.name

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

每個 cacheGroup 也可以使用:splitChunks.cacheGroups.{cacheGroup}.name。

拆分 chunk 的名稱。設為 false 將保持 chunk 的相同名稱,因此不會不必要地更改名稱。這是生產(chǎn)環(huán)境下構(gòu)建的建議值。

提供字符串或函數(shù)使你可以使用自定義名稱。指定字符串或始終返回相同字符串的函數(shù)會將所有常見模塊和 vendor 合并為一個 chunk。這可能會導致更大的初始下載量并減慢頁面加載速度。

如果你選擇指定一個函數(shù),則可能會發(fā)現(xiàn) chunk.name 和 chunk.hash 屬性(其中 chunk 是 chunks 數(shù)組的一個元素)在選擇 chunk 名時特別有用。

如果 splitChunks.name 與 entry point 名稱匹配,entry point 將被刪除。

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

?boolean = true?

弄清哪些 export 被模塊使用,以混淆 export 名稱,省略未使用的 export,并生成有效的代碼。 當它為 true 時:分析每個運行時使用的出口,當它為 "global" 時:分析所有運行時的全局 export 組合)。

splitChunks.cacheGroups

緩存組可以繼承和/或覆蓋來自 splitChunks.* 的任何選項。但是 test、priority 和 reuseExistingChunk 只能在緩存組級別上進行配置。將它們設置為 false以禁用任何默認緩存組。

webpack.config.js

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

splitChunks.cacheGroups.{cacheGroup}.priority

?number = -20?

一個模塊可以屬于多個緩存組。優(yōu)化將優(yōu)先考慮具有更高 priority(優(yōu)先級)的緩存組。默認組的優(yōu)先級為負,以允許自定義組獲得更高的優(yōu)先級(自定義組的默認值為 0)。

splitChunks.cacheGroups.{cacheGroup}.reuseExistingChunk

?boolean = true?

如果當前 chunk 包含已從主 bundle 中拆分出的模塊,則它將被重用,而不是生成新的模塊。這可能會影響 chunk 的結(jié)果文件名。

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?

控制此緩存組選擇的模塊。省略它會選擇所有模塊。它可以匹配絕對模塊資源路徑或 chunk 名稱。匹配 chunk 名稱時,將選擇 chunk 中的所有模塊。

為 ?{cacheGroup}.test? 提供一個函數(shù):

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';
          },
        },
      },
    },
  },
};

為了查看 module and chunks 對象中可用的信息,你可以在回調(diào)函數(shù)中放入 debugger; 語句。然后 以調(diào)試模式運行 webpack 構(gòu)建 檢查 Chromium DevTools 中的參數(shù)。

向 ?{cacheGroup}.test? 提供 RegExp:

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

?string? ?function (pathData, assetInfo) => string?

僅在初始 chunk 時才允許覆蓋文件名。 也可以在 output.filename 中使用所有占位符。

webpack.config.js

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

若為函數(shù),則:

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'?,可以創(chuàng)建文件夾結(jié)構(gòu)。

webpack.config.js

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

splitChunks.cacheGroups.{cacheGroup}.enforce

?boolean = false?

告訴 webpack 忽略 splitChunks.minSize、splitChunks.minChunks、splitChunks.maxAsyncRequests 和 splitChunks.maxInitialRequests 選項,并始終為此緩存組創(chuàng)建 chunk。

webpack.config.js

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

splitChunks.cacheGroups.{cacheGroup}.idHint

?string?

設置 chunk id 的提示。 它將被添加到 chunk 的文件名中。

webpack.config.js

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

Examples

Defaults: Example 1

// index.js

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

//...

結(jié)果: 將創(chuàng)建一個單獨的包含 react 的 chunk。在導入調(diào)用中,此 chunk 并行加載到包含 ./a 的原始 chunk 中。

為什么:

  • 條件1:chunk 包含來自 node_modules 的模塊
  • 條件2:react 大于 30kb
  • 條件3:導入調(diào)用中的并行請求數(shù)為 2
  • 條件4:在初始頁面加載時不影響請求

這背后的原因是什么?react 可能不會像你的應用程序代碼那樣頻繁地更改。通過將其移動到單獨的 chunk 中,可以將該 chunk 與應用程序代碼分開進行緩存(假設你使用的是 chunkhash,records,Cache-Control 或其他長期緩存方法)。

Defaults: Example 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

//...

結(jié)果: 將創(chuàng)建一個單獨的 chunk,其中包含 ./helpers 及其所有依賴項。在導入調(diào)用時,此 chunk 與原始 chunks 并行加載。

為什么:

  • 條件1:chunk 在兩個導入調(diào)用之間共享
  • 條件2:helpers 大于 30kb
  • 條件3:導入調(diào)用中的并行請求數(shù)為 2
  • 條件4:在初始頁面加載時不影響請求

將 helpers 的內(nèi)容放入每個 chunk 中將導致其代碼被下載兩次。通過使用單獨的塊,這只會發(fā)生一次。我們會進行額外的請求,這可以視為一種折衷。這就是為什么最小體積為 30kb 的原因。

Split Chunks: Example 1

創(chuàng)建一個 commons chunk,其中包括入口(entry points)之間所有共享的代碼。

webpack.config.js

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

Split Chunks: Example 2

創(chuàng)建一個 vendors chunk,其中包括整個應用程序中 node_modules 的所有代碼。

webpack.config.js

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

Split Chunks: Example 3

創(chuàng)建一個 custom vendor chunk,其中包含與 RegExp 匹配的某些 node_modules 包。

webpack.config.js

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

Further Reading


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號