SplitChunksPlugin

元々は、チャンク(と、その中でインポートされたモジュール)は、内部webpackグラフにおいて親子関係によって接続されていました。CommonsChunkPluginは、それら全体で重複する依存関係を回避するために使用されていましたが、それ以上の最適化は不可能でした。

webpack v4以降、CommonsChunkPluginoptimization.splitChunksに置き換えられました。

デフォルト値

すぐに使えるSplitChunksPluginは、ほとんどのユーザーにとってうまく機能するはずです。

デフォルトでは、オンデマンドチャンクのみに影響します。初期チャンクを変更すると、プロジェクトの実行に必要なHTMLファイルに含めるスクリプトタグに影響するためです。

Webpackは、これらの条件に基づいてチャンクを自動的に分割します。

  • 新しいチャンクは共有できるか、モジュールが`node_modules`フォルダにあるかのいずれかです。
  • 新しいチャンクのサイズが20KBを超える場合(最小化+gz圧縮前)。
  • オンデマンドでチャンクをロードする場合の並列リクエストの最大数が30以下である場合。
  • 初期ページロード時の並列リクエストの最大数が30以下である場合。

最後の2つの条件を満たそうとする場合、より大きなチャンクが優先されます。

設定

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'` `関数 (チャンク)` `正規表現`

これは、最適化の対象となるチャンクを示します。文字列が指定されている場合、有効な値は`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/,
    },
  },
};

splitChunks.maxAsyncRequests

数値 = 30

オンデマンドロード時の並列リクエストの最大数。

splitChunks.maxInitialRequests

数値 = 30

エントリポイントでの並列リクエストの最大数。

splitChunks.defaultSizeTypes

[文字列] = ['javascript', 'unknown']

数値がサイズに使用される場合に使用されるサイズの種類を設定します。

splitChunks.minChunks

数値 = 1

分割する前に、モジュールをチャンク間で共有する必要がある最小回数。

splitChunks.hidePathInfo

ブール値

maxSizeで分割された部分の名前を作成する際に、パス情報を公開しないようにします。

splitChunks.minSize

`数値 = 20000` `{ [index: 文字列]: 数値 }`

生成されるチャンクの最小サイズ(バイト単位)。

splitChunks.minSizeReduction

`数値` `{ [index: 文字列]: 数値 }`

チャンクを生成するために必要な、メインチャンク(バンドル)への最小サイズ削減量(バイト単位)。つまり、チャンクに分割してもメインチャンク(バンドル)のサイズが指定されたバイト数だけ削減されない場合、`splitChunks.minSize`の値を満たしていても分割されません。

splitChunks.enforceSizeThreshold

`splitChunks.cacheGroups.{cacheGroup}.enforceSizeThreshold`

数値 = 50000

分割が強制され、他の制限(minRemainingSize、maxAsyncRequests、maxInitialRequests)が無視されるサイズしきい値。

splitChunks.minRemainingSize

`splitChunks.cacheGroups.{cacheGroup}.minRemainingSize`

数値 = 0

`splitChunks.minRemainingSize`オプションは、分割後のチャンクの最小サイズが制限を超えていることを確認することで、サイズ0のモジュールを回避するためにWebpack 5で導入されました。'development'モードではデフォルトで`0`になります。それ以外の場合は、`splitChunks.minRemainingSize`は`splitChunks.minSize`の値をデフォルトで使用するため、深い制御が必要なまれな場合を除いて、手動で指定する必要はありません。

splitChunks.layer

`splitChunks.cacheGroups.{cacheGroup}.layer`

`正規表現` `文字列` `関数`

モジュールレイヤー別にキャッシュグループにモジュールを割り当てます。

splitChunks.maxSize

数値 = 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と長期キャッシングで使用することを目的としています。リクエスト数を増やすことで、キャッシングを改善します。また、ファイルサイズを小さくすることで、再構築を高速化することもできます。

splitChunks.maxAsyncSize

数値

`maxSize`と同様に、`maxAsyncSize`はグローバル(`splitChunks.maxAsyncSize`)、キャッシュグループ(`splitChunks.cacheGroups.{cacheGroup}.maxAsyncSize`)、またはフォールバックキャッシュグループ(`splitChunks.fallbackCacheGroup.maxAsyncSize`)に適用できます。

`maxAsyncSize`と`maxSize`の違いは、`maxAsyncSize`はオンデマンドロードチャンクのみに影響することです。

splitChunks.maxInitialSize

数値

`maxSize`と同様に、`maxInitialSize`はグローバル(`splitChunks.maxInitialSize`)、キャッシュグループ(`splitChunks.cacheGroups.{cacheGroup}.maxInitialSize`)、またはフォールバックキャッシュグループ(`splitChunks.fallbackCacheGroup.maxInitialSize`)に適用できます。

`maxInitialSize`と`maxSize`の違いは、`maxInitialSize`は初期ロードチャンクのみに影響することです。

splitChunks.name

`ブール値 = 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.usedExports

splitChunks.cacheGroups{cacheGroup}.usedExports

boolean = true

モジュールが使用するエクスポートを特定し、エクスポート名の難読化、未使用エクスポートの省略を行い、より効率的なコードを生成します。true の場合、各ランタイムで使用されるエクスポートを分析し、"global" の場合、すべてのランタイムを組み合わせてグローバルにエクスポートを分析します。

splitChunks.cacheGroups

キャッシュグループは、splitChunks.* から任意のオプションを継承および/またはオーバーライドできます。ただし、testpriorityreuseExistingChunk は、キャッシュグループレベルでのみ設定できます。デフォルトのキャッシュグループを無効にするには、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.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 オブジェクトで使用可能な情報を確認するには、コールバックに debugger; ステートメントを配置します。次に、デバッグモードでWebpackビルドを実行して、Chromium DevToolsでパラメーターを検査します。

{cacheGroup}.testRegExp を指定する場合

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.minSizesplitChunks.minChunkssplitChunks.maxAsyncRequestssplitChunks.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',
        },
      },
    },
  },
};

デフォルト:例1

// index.js

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

//...

結果:reactを含む個別のチャンクが作成されます。インポート時に、このチャンクは./aを含む元のチャンクと並列にロードされます。

理由

  • 条件1:チャンクにnode_modulesからのモジュールが含まれている
  • 条件2:reactが30kbより大きい
  • 条件3:インポート時の並列リクエスト数が2
  • 条件4:初期ページロードのリクエストには影響しない

その理由は何ですか?reactはおそらくアプリケーションコードほど頻繁に変更されません。別のチャンクに移動することで、このチャンクはアプリケーションコードとは別にキャッシュできます(chunkhash、レコード、Cache-Control、その他の長期キャッシュアプローチを使用していることを前提としています)。

デフォルト:例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の内容を各チャンクに入れると、そのコードが2回ダウンロードされることになります。個別のチャンクを使用することで、これは1回だけになります。追加のリクエストのコストがかかりますが、これはトレードオフと考えることができます。そのため、最小サイズが30kbになっています。

チャンク分割:例1

エントリポイント間で共有されるすべてのコードを含むcommonsチャンクを作成します。

webpack.config.js

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

チャンク分割:例2

アプリケーション全体のnode_modulesからのすべてのコードを含むvendorsチャンクを作成します。

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