コード分割

JavaScript バンドルを複数のチャンクに分割し、必要な部分だけを読み込む最適化手法

パフォーマンスフロントエンド

コード分割とは

コード分割 (Code Splitting) は、JavaScript バンドルをルートごと、コンポーネントごとに複数のチャンクに分割し、初期ロード時に必要なコードだけを読み込む最適化手法である。SPA の初期バンドルサイズを削減し、LCP (Largest Contentful Paint) を改善する。

なぜ必要か

コード分割なし:
  bundle.js (2MB) ← 全ページのコードを含む
  初期ロード: 2MB ダウンロード → パース → 実行

コード分割あり:
  main.js (200KB) ← 共通コード + 現在のページ
  about.chunk.js (50KB) ← /about ページ用 (遅延読み込み)
  admin.chunk.js (300KB) ← /admin ページ用 (遅延読み込み)
  初期ロード: 200KB のみ

React での実装

React.lazy + Suspense

import { lazy, Suspense } from 'react';

// 動的インポート: /admin にアクセスした時だけ読み込む
const AdminPage = lazy(() => import('./pages/AdminPage'));
const SettingsPage = lazy(() => import('./pages/SettingsPage'));

function App() {
  return (
    <Suspense fallback={<Loading />}>
      <Routes>
        <Route path="/" element={<HomePage />} />
        <Route path="/admin" element={<AdminPage />} />
        <Route path="/settings" element={<SettingsPage />} />
      </Routes>
    </Suspense>
  );
}

Next.js の自動コード分割

Next.js はページごとに自動的にコード分割する。pages/about.tsx/about にアクセスした時だけ読み込まれる。next/dynamic で コンポーネントレベルの分割も可能。

import dynamic from 'next/dynamic';

const HeavyChart = dynamic(() => import('../components/HeavyChart'), {
  loading: () => <p>Loading chart...</p>,
  ssr: false,  // サーバーサイドではレンダリングしない
});

分割の粒度

粒度 方法 効果
ルートレベル ページごとに分割 最も効果的、必須
コンポーネントレベル 重いコンポーネントを遅延読み込み モーダル、チャート、エディタ
ライブラリレベル 大きなライブラリを別チャンクに moment.js, lodash

Vite / webpack の設定

// vite.config.ts: マニュアルチャンク分割
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
          charts: ['recharts'],
        },
      },
    },
  },
});

プリフェッチ

コード分割したチャンクを、ユーザーが遷移する前にバックグラウンドで先読みする。

<!-- ユーザーが /admin に遷移しそうな時に先読み -->
<link rel="prefetch" href="/admin.chunk.js">

Next.js の <Link> コンポーネントは、ビューポートに入ったリンク先のチャンクを自動的にプリフェッチする。

実務での活用方法は関連書籍にも詳しい。

関連用語