元々は、チャンク(と、その中でインポートされたモジュール)は、内部webpackグラフにおいて親子関係によって接続されていました。CommonsChunkPlugin
は、それら全体で重複する依存関係を回避するために使用されていましたが、それ以上の最適化は不可能でした。
webpack v4以降、CommonsChunkPlugin
はoptimization.splitChunks
に置き換えられました。
すぐに使えるSplitChunksPlugin
は、ほとんどのユーザーにとってうまく機能するはずです。
デフォルトでは、オンデマンドチャンクのみに影響します。初期チャンクを変更すると、プロジェクトの実行に必要なHTMLファイルに含めるスクリプトタグに影響するためです。
Webpackは、これらの条件に基づいてチャンクを自動的に分割します。
最後の2つの条件を満たそうとする場合、より大きなチャンクが優先されます。
Webpackは、この機能をより詳細に制御したい開発者向けに、一連のオプションを提供しています。
この設定オブジェクトは、`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,
},
},
},
},
};
文字列 = '~'
デフォルトでは、Webpackはチャンクのオリジンと名前を使用して名前を生成します(例:`vendors~main.js`)。このオプションを使用すると、生成される名前で使用されるデリミタを指定できます。
`文字列 = 'async'` `関数 (チャンク)` `正規表現`
これは、最適化の対象となるチャンクを示します。文字列が指定されている場合、有効な値は`all`、`async`、`initial`です。`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/,
},
},
};
数値 = 30
オンデマンドロード時の並列リクエストの最大数。
数値 = 30
エントリポイントでの並列リクエストの最大数。
[文字列] = ['javascript', 'unknown']
数値がサイズに使用される場合に使用されるサイズの種類を設定します。
数値 = 1
分割する前に、モジュールをチャンク間で共有する必要がある最小回数。
ブール値
maxSizeで分割された部分の名前を作成する際に、パス情報を公開しないようにします。
`数値 = 20000` `{ [index: 文字列]: 数値 }`
生成されるチャンクの最小サイズ(バイト単位)。
`数値` `{ [index: 文字列]: 数値 }`
チャンクを生成するために必要な、メインチャンク(バンドル)への最小サイズ削減量(バイト単位)。つまり、チャンクに分割してもメインチャンク(バンドル)のサイズが指定されたバイト数だけ削減されない場合、`splitChunks.minSize`の値を満たしていても分割されません。
数値 = 50000
分割が強制され、他の制限(minRemainingSize、maxAsyncRequests、maxInitialRequests)が無視されるサイズしきい値。
数値 = 0
`splitChunks.minRemainingSize`オプションは、分割後のチャンクの最小サイズが制限を超えていることを確認することで、サイズ0のモジュールを回避するためにWebpack 5で導入されました。'development'モードではデフォルトで`0`になります。それ以外の場合は、`splitChunks.minRemainingSize`は`splitChunks.minSize`の値をデフォルトで使用するため、深い制御が必要なまれな場合を除いて、手動で指定する必要はありません。
`正規表現` `文字列` `関数`
モジュールレイヤー別にキャッシュグループにモジュールを割り当てます。
数値 = 0
`maxSize`を使用すると(グローバルに`optimization.splitChunks.maxSize`、キャッシュグループごとに`optimization.splitChunks.cacheGroups[x].maxSize`、またはフォールバックキャッシュグループに`optimization.splitChunks.fallbackCacheGroup.maxSize`)、`maxSize`バイトより大きいチャンクをより小さな部分に分割しようとします。部分は少なくとも`minSize`(`maxSize`の隣)のサイズになります。アルゴリズムは決定論的であり、モジュールへの変更はローカルな影響しかありません。そのため、長期キャッシングに使用でき、レコードは必要ありません。`maxSize`はヒントに過ぎず、モジュールが`maxSize`より大きい場合、または分割によって`minSize`に違反する場合には、違反される可能性があります。
チャンクに既に名前がある場合、各部分は、その名前から派生した新しい名前を取得します。`optimization.splitChunks.hidePathInfo`の値に応じて、最初のモジュール名から派生したキー、またはそのハッシュを追加します。
`maxSize`オプションは、HTTP/2と長期キャッシングで使用することを目的としています。リクエスト数を増やすことで、キャッシングを改善します。また、ファイルサイズを小さくすることで、再構築を高速化することもできます。
数値
`maxSize`と同様に、`maxAsyncSize`はグローバル(`splitChunks.maxAsyncSize`)、キャッシュグループ(`splitChunks.cacheGroups.{cacheGroup}.maxAsyncSize`)、またはフォールバックキャッシュグループ(`splitChunks.fallbackCacheGroup.maxAsyncSize`)に適用できます。
`maxAsyncSize`と`maxSize`の違いは、`maxAsyncSize`はオンデマンドロードチャンクのみに影響することです。
数値
`maxSize`と同様に、`maxInitialSize`はグローバル(`splitChunks.maxInitialSize`)、キャッシュグループ(`splitChunks.cacheGroups.{cacheGroup}.maxInitialSize`)、またはフォールバックキャッシュグループ(`splitChunks.fallbackCacheGroup.maxInitialSize`)に適用できます。
`maxInitialSize`と`maxSize`の違いは、`maxInitialSize`は初期ロードチャンクのみに影響することです。
`ブール値 = false` `関数 (モジュール、チャンク、キャッシュグループキー) => 文字列` `文字列`
各キャッシュグループにも使用できます:`splitChunks.cacheGroups.{cacheGroup}.name`。
分割チャンクの名前。`false`を指定すると、チャンクの名前が不要に変更されないため、同じ名前が保持されます。これは本番ビルドに推奨される値です。
文字列または関数を指定すると、カスタム名を使用できます。常に同じ文字列を返す文字列または関数を指定すると、すべての共通モジュールとベンダーが単一のチャンクにマージされます。これにより、初期ダウンロードが大きくなり、ページの読み込みが遅くなる可能性があります。
関数を指定する場合、チャンクの名前を選択する際に`chunk.name`プロパティ(`chunk`は`chunks`配列の要素)が特に役立ちます。
`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を実行すると、次の名前のcommonグループのチャンクも出力されます:`commons-main-lodash.js.e7519d2bb8777058fa27.js`(ハッシュは実際の出力例として示されています)。
splitChunks.cacheGroups{cacheGroup}.usedExports
boolean = true
モジュールが使用するエクスポートを特定し、エクスポート名の難読化、未使用エクスポートの省略を行い、より効率的なコードを生成します。true
の場合、各ランタイムで使用されるエクスポートを分析し、"global"
の場合、すべてのランタイムを組み合わせてグローバルにエクスポートを分析します。
キャッシュグループは、splitChunks.*
から任意のオプションを継承および/またはオーバーライドできます。ただし、test
、priority
、reuseExistingChunk
は、キャッシュグループレベルでのみ設定できます。デフォルトのキャッシュグループを無効にするには、false
に設定します。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
default: false,
},
},
},
};
splitChunks.cacheGroups.{cacheGroup}.priority
number = -20
モジュールは複数のキャッシュグループに属することができます。最適化では、priority
が高いキャッシュグループが優先されます。デフォルトのグループは負の優先度を持ち、カスタムグループがより高い優先度を持つことを可能にします(カスタムグループのデフォルト値は0
です)。
splitChunks.cacheGroups.{cacheGroup}.reuseExistingChunk
boolean = 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.{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';
},
},
},
},
},
};
module
と chunks
オブジェクトで使用可能な情報を確認するには、コールバックに debugger;
ステートメントを配置します。次に、デバッグモードでWebpackビルドを実行して、Chromium DevToolsでパラメーターを検査します。
{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
初期チャンクの場合にのみ、ファイル名をオーバーライドできます。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
boolean = false
splitChunks.minSize
、splitChunks.minChunks
、splitChunks.maxAsyncRequests
、splitChunks.maxInitialRequests
オプションを無視し、このキャッシュグループのチャンクを常に作成するようにWebpackに指示します。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
defaultVendors: {
enforce: true,
},
},
},
},
};
splitChunks.cacheGroups.{cacheGroup}.idHint
string
チャンクIDのヒントを設定します。チャンクのファイル名に追加されます。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
defaultVendors: {
idHint: 'vendors',
},
},
},
},
};
// index.js
import('./a'); // dynamic import
// a.js
import 'react';
//...
結果:react
を含む個別のチャンクが作成されます。インポート時に、このチャンクは./a
を含む元のチャンクと並列にロードされます。
理由
node_modules
からのモジュールが含まれているreact
が30kbより大きいその理由は何ですか?react
はおそらくアプリケーションコードほど頻繁に変更されません。別のチャンクに移動することで、このチャンクはアプリケーションコードとは別にキャッシュできます(chunkhash、レコード、Cache-Control、その他の長期キャッシュアプローチを使用していることを前提としています)。
// 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
とそのすべての依存関係を含む個別のチャンクが作成されます。インポート時に、このチャンクは元のチャンクと並列にロードされます。
理由
helpers
が30kbより大きいhelpers
の内容を各チャンクに入れると、そのコードが2回ダウンロードされることになります。個別のチャンクを使用することで、これは1回だけになります。追加のリクエストのコストがかかりますが、これはトレードオフと考えることができます。そのため、最小サイズが30kbになっています。
エントリポイント間で共有されるすべてのコードを含むcommons
チャンクを作成します。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
commons: {
name: 'commons',
chunks: 'initial',
minChunks: 2,
},
},
},
},
};
アプリケーション全体のnode_modules
からのすべてのコードを含むvendors
チャンクを作成します。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
},
};
RegExp
で一致する特定のnode_modules
パッケージを含むカスタムベンダー
チャンクを作成します。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
name: 'vendor',
chunks: 'all',
},
},
},
},
};