定義
複数のページが共通で使う外部ライブラリ(node_modules)のコードを1つにまとめてキャッシュしようとして、かえって すべてのページが巨大な共通のかたまりを丸ごとダウンロードしてしまう 罠を避けたいときに知っておくべき概念だ。
まず用語を解くと:バンドルは webpack が複数のファイルを結合して作る1つの JS ファイル、**チャンク(chunk)**はそのバンドルを分割した断片、vendor チャンクは外部ライブラリだけを集めた断片、First Load は1つのページを最初に開くときに受け取らねばならない JS の総量、dynamic import(import()・next/dynamic)は「必要なときに別途ダウンロードさせる」ようコードを分割する手法だ。
具体的には、optimization.splitChunks.cacheGroups(webpack でどのモジュールをどのチャンクにまとめるか決める設定)のすべてを受け止める vendor グループに 固定の name と chunks: "all" を一緒に与えると、dynamic import でせっかく切り出したモジュールまで 初期の共通 vendor チャンクへ再び引き込まれ(hoist)、すべてのページの First Load が肥大化する。つまり、dynamic import だけでは webpack の分割ポリシーに勝てないことがある。
なぜ必要か
バンドル最適化は「ルートごとに何が違うか」より先に、すべてのエントリが共有するチャンクを疑うべきだ。First Load がルートごとにほぼ同じなら、共有 vendor がボトルネックの可能性が高い。async cacheGroup(非同期チャンク用のまとめ規則)を追加しても、そのモジュールが依然として static import チェーン(ページが最初から import で直接引き込む経路)に残っていれば、初期グラフから外れない。
動作原理
- webpack はモジュールグラフを解析し、cacheGroup
testに合うモジュールをチャンクにまとめる。 chunks: "all"だと async チャンクにあるモジュールもそのグループの候補になる。- 固定の
name: "vendor"は複数のエントリ・ルート専用の依存を 1つのファイルに強制する。 next/dynamicなどでコンポーネントを lazy にしても、同一モジュールが2箇所以上から参照されると splitChunks が shared に引き上げることがある。- named async cacheGroup(
enforce: true)は すでに初期グラフにある import を外せない — import 経路そのものを async 化する必要がある。
実務での適用
@next/bundle-analyzerまたは webpack-bundle-analyzer で vendor 内部のモジュール一覧を見る。CLI の「shared by all」の合計だけでは足りないことが多い。- catch-all vendor に
chunks: "initial"、固定nameを除去、maxSizeを適用。 - 重いライブラリ(数式・アニメーション・キャンバスなど)は async cacheGroup + 使用コンポーネントの dynamic import。
- cacheGroup
testの正規表現が wrapper パッケージではなく 実際にバンドルに入るパッケージ名を指しているか確認する。 - 変更ごとに再ビルド・gzip 測定で効果を検証する。
トレードオフ
- vendor 分割を増やすと shared チャンク数・HTTP 要求が増えることがある。HTTP/2・キャッシュヒットと First Load の目標の間でバランスを取る。
- 過度な cacheGroup はビルド時間・デバッグの複雑さを上げる。すでにルート単位で lazy な大型チャンクは、さらなる分割の実益が小さいことがある。
- Turbopack・Rspack などバンドラごとに既定の split ポリシーが異なる — webpack のカスタム設定はそのバンドラのドキュメントと照合する。
使ってはいけない場合
- すでに First Load が小さくルートごとに大きく異なる場合 — 共有 vendor が主因でないことがある。
- 測定なしで cacheGroup をコピペ — 環境・依存グラフが違えば逆効果になりうる。
- すべての node_modules を無条件で initial vendor から除外しようとする試み — ランタイムチャンクの爆発・循環参照のリスク。
よくある間違い
- dynamic import を追加後「分離された」と思い込み、analyzer を再確認しない。
- async cacheGroup だけ追加して上位で static import を維持する。
lottie-reactだけをマッチさせ、実際に重い core パッケージは vendor に残す。- ビルドログの
error文字列で完了を誤検知する(ツールの警告メッセージを含む)。
関連概念
- resource-priority — ロード優先度・critical path
- lab-performance-measurement-variance — バンドル・メトリクス測定の分散