Featured image of post JavaScriptバンドルサイズ最適化:分析から実行まで Featured image of post JavaScriptバンドルサイズ最適化:分析から実行まで

JavaScriptバンドルサイズ最適化:分析から実行まで

JavaScriptバンドルサイズ最適化の包括的ガイド。バンドル分析、ツリーシェイキング、コード分割、動的インポート、ESM出力、監視戦略まで網羅。

JavaScriptのバンドルサイズはユーザー体験に直接影響します。サイズが大きいほどダウンロード時間とパース・コンパイル時間が増加し、Core Web Vitalsが悪化します。JavaScriptが100KB増加するごとにコンバージョン率は2〜3%低下するというデータもあります。バンドル最適化は一度きりの対応ではなく、分析、特定、最適化、監視のサイクルを継続的に回す投資です。

バンドル分析ツール

バンドルに何が含まれているかを把握することが第一歩です。webpack-bundle-analyzerはインタラクティブなツリーマップを提供し、大きな依存関係や重複モジュールを視覚的に特定できます。Viteではrollup-plugin-visualizerがサンバーストやネットワークグラフを表示し、esbuildは–metafileフラグで詳細な出力分析が可能です。source-map-explorerを使えばコンパイル済みコードを元のソースファイルにマッピングできます。

# webpackのバンドル分析レポートを生成
npx webpack --profile --json > stats.json
npx webpack-bundle-analyzer stats.json

分析結果を確認する際は、過剰に大きな依存関係、チャンク間の重複モジュール、予期しないポリフィル、不要なコードを含む古いライブラリバージョンに注目します。典型的な最適化では、これらの発見に基づいて1.2MBのバンドルを480KB以下に削減できます。


ツリーシェイキング詳解

ツリーシェイキングはESモジュールのimport文とexport文を静的に解析し、未使用のエクスポートを除去します。効果的なツリーシェイキングには3つの条件が必要です。ESM構文のみを使用すること、package.jsonで"sideEffects": falseを宣言すること、名前空間インポートを避けた適切なインポートスタイルを採用することです。

{
  "sideEffects": false,
  "exports": {
    ".": "./src/index.js",
    "./utils": "./src/utils/index.js"
  }
}

よくある誤解として、import * asがツリーシェイキングを妨げる、バレルファイルからのインポートは安全、CSSインポートは干渉しないというものがありますが、これらはすべて状況次第です。lodash-eslodashよりはるかにツリーシェイキングが効き、date-fnsは16KBのgzipサイズに対してmoment.jsは231KBと大きな差があります。

コード分割戦略

コード分割は複数のレベルで機能します。エントリポイント分割ではベンダーコード、アプリケーションコード、ランタイムコードを別々のバンドルに分離します。動的インポートによるルートベースの分割はReact.lazyとSuspenseで実現し、リッチテキストエディタやチャートといった重量級UI要素はコンポーネントレベルで分割します。

const Dashboard = React.lazy(() => import('./routes/Dashboard'));
const Analytics = React.lazy(() => import('./routes/Analytics'));

preloadとprefetchヒントにより読み込みを最適化します。実際の事例では、ダッシュボードアプリケーションを8つのルートチャンクに分割することで、初期バンドルを65%削減しながらセカンダリルートへの瞬時の移動を維持しました。

動的インポートパターン

条件付きインポートにより、ブラウザがネイティブ対応していない場合にのみポリフィルを読み込むことができます。環境別インポートで開発専用ツールを本番から除外し、フィーチャーフラグベースのインポートで複数のアプリケーションバージョンをデプロイせずにA/Bテストを実現します。

if (!('group' in Array.prototype)) {
  await import('core-js/actual/array/group');
}

webpackのマジックコメントを使用すると、チャンク名やプリフェッチ動作を細かく制御でき、webpackExportsにより動的インポート内でもツリーシェイキングを適用できます。


デッドコード除去と依存関係プロファイリング

ts-pruneやunimportedを使用して、インポートされていても実際には呼び出されていないエクスポートを特定します。cost-of-modulesやpackage-sizeといった依存関係プロファイラーは各ライブラリのバンドル影響を定量化し、API品質だけでなくサイズも考慮したライブラリ選択を促進します。

ライブラリgzipサイズ代替案gzipサイズ
moment.js231KBdayjs6KB
lodash71KBlodash-es24KB
redux12KBzustand3KB

依存関係を削減するには、軽量な代替ライブラリの採用、小さなユーティリティ関数の自前実装、変更頻度の低い大規模ライブラリへのCDN Subresource Integrityの活用が有効です。

モダン形式出力

ESMはモダンなモジュール形式であり、優れたツリーシェイキング、静的解析、ブラウザネイティブのモジュール読み込みを提供します。デュアルESM/CJSパッケージはpackage.jsonのexportsフィールドで条件付きエクスポートを実現します。アプリケーションバンドルでは、モダンブラウザ向けにESMを出力し、typeモジュールとnomoduleパターンでレガシーブラウザ向けのフォールバックを提供します。ViteはESMファーストのアプローチを採用し、webpackはCJS互換性レイヤーを維持しています。

監視と予算強制

監視なしの最適化は持続不可能です。Lighthouse CIがバンドルサイズの回帰を検出し、GitHub Actionsとsize-limitやbundlesizeの組み合わせでPRに予算を強制できます。

{
  "scripts": {
    "size": "size-limit"
  },
  "size-limit": [
    {
      "path": "dist/main.js",
      "limit": "170 KB",
      "running": false
    }
  ]
}

PerformanceObserver APIを通じてスクリプト評価時間と長いタスクを追跡するリアルユーザーモニタリングにより、ルート別の予算と95パーセンタイルのJavaScript実行時間を監視します。

高度な技術

webpackのsplitChunks.cacheGroupsやViteのmanualChunksによる手動チャンク分割、CSS-in-JS抽出によるランタイムオーバーヘッドの排除、core-jsとuseBuiltIns usageによる最小限のポリフィル戦略が標準パターンを超えた最適化です。WebAssemblyモジュールは標準のバンドルアナライザでは把握できないため、個別のサイズプロファイリングが必要です。

結論

バンドル最適化は継続的に測定しながら進める反復プロセスです。分析ツールをセットアップし、最大の貢献要因を特定し、ターゲットを絞った最適化を適用し、CI/CDでの監視を自動化します。バンドルサイズを重要な関心事として扱うチームは、より高速な体験、優れたCore Web Vitalsスコア、そして高いコンバージョン率を実現します。