Contents
はじめに:この記事の目的と読み方
この記事は webpack、Vite、Rollup の JavaScript モジュール バンドラー 比較を実務視点で整理し、チームで採用判断できる資料を提供することを目的とします。導入前チェック、再現可能なベンチ手順、KPIの定義、移行テンプレートやCI例を具体的に示します。短時間で評価できる手順と測定方法を明示し、JavaScript モジュール バンドラー 比較を再現可能にします。
要点(短くまとめ)
ここで最初に要点を示します。
- 開発体験(起動・HMR)を重視するなら Vite を候補とする。
- ライブラリ開発では Rollup の出力とツリーシェイキングが有利。
- マイクロフロントや細かなランタイム最適化が必要なら Webpack(Module Federation)を検討する。
- いずれを採用する場合も、必ずPoCでKPIを定量測定し、中央値・標準偏差・rawデータを保存する。
導入前チェックリスト(バンドラー選定の前提)
選定前に要件を明確化すると比較が定量化できます。ここではプロジェクトタイプ、チームスキル、ブラウザ要件、CI要件、KPIの決め方を示します。KPIは後述の測定手順で必ず再現してください。
プロジェクトタイプの確認
プロジェクトの種類を明確に分類してください。要件により重視すべき点が変わります。
- SPA:開発時の起動速度とHMRが最重要です。初期ロード最適化も考慮します。
- MPA:複数エントリのビルド効率とキャッシュ戦略を重視します。
- ライブラリ:ESM出力とツリーシェイキング効率、複数フォーマット出力(esm/cjs/umd)が重要です。
- SSR/Edge:サーババンドルの互換性やランタイム依存を優先します。
- モノリポ:並列ビルドとキャッシュ永続化の設計が必要です。
チームスキルと保守性
チームの経験値で選択は変わります。保守コストと学習コストを見積もってください。
- Webpack:高い柔軟性だが設定と最適化に知見が必要です。
- Vite:デフォルトで良好な開発体験を提供し、設定コストが低めです。
- Rollup:ライブラリ向けに安定した出力を得やすく、比較的シンプルです。
ブラウザ互換性とポリフィル要件
サポート対象ブラウザを決めるとビルド負荷が変わります。
- モダンブラウザのみ:ESMを前提に軽い変換で済みます。
- レガシー対応(例: IE相当):大規模なトランスパイルとポリフィルでサイズ増・時間増が発生します。
- 対応方法:browserslist をベースにし、必要なポリフィルをCIで明確化してください。
CI要件とビルドSLA
CIでの要件を明確にしておくと選定に直結します。
- 並列ビルドやアーティファクト保存、キャッシュ戦略の要否を決める。
- キャッシュキーに lockfile ハッシュと Node バージョンを含めると再現性が高まります。
- ビルド時間のSLA(例: 本番ビルド cold median < 60s)を定めます。
パフォーマンス目標(KPIと測定方法)
KPIは具体的な閾値か、環境依存なら測定方法を定義してください。下表は実務で使える例と測定方法です。
パフォーマンスKPIと測定方法
| KPI | 目安(例) | 測定方法 | 備考 |
|---|---|---|---|
| 開発サーバ起動(cold) | 小規模SPA: median < 1.0s / 中規模: median < 3.0s | プロセス起動→コンソールの "ready" または最初のHMR handshake完了までを5〜7回測定し中央値を採る(hyperfine / スクリプト) | Node 18 + 6コア以上 + NVMe SSD を想定した目安 |
| HMR 往復(軽微変更) | 小規模: <100ms / 中規模: <300ms | ファイル保存→クライアント側の HMR 更新受信までを複数回計測し中央値を採る(Puppeteer 等で自動化) | ネットワークレイテンシを除外するため同一マシンで測定推奨 |
| 本番初期JS(gzipped) | SPA 目安: 100KB〜200KB 以下を目標 | ビルド後に gzip/brotli サイズを計測(gzip-size / brotli-size CLI) | 機能によって許容差あり。ライブラリは 50KB 未満を目指す |
| 本番ビルド時間(cold) | 小: <15s / 中: <60s / 大: <300s | キャッシュ削除後にビルドを3〜7回実行し中央値を採る(hyperfine 推奨) | warm build も別途計測すること |
| ツリーシェイキング効率 | 削減率 >70% を目標(ライブラリ) | bundle analyzer(rollup-plugin-visualizer / webpack-bundle-analyzer)で未使用コード比を計測 | CJS/ESM 混在で精度が下がる場合あり |
各バンドラーの概要と設計思想(Webpack / Vite / Rollup)
主要バンドラーの設計思想を押さえると採用判断が速くなります。代表的なメジャーバージョン例(Webpack 5系 / Vite 4系 / Rollup 3系)を参照し、実運用では利用する正確なバージョンのドキュメントを確認してください。
Webpack の特徴と使いどころ
Webpack は Loader/Plugin ベースで高度にカスタマイズ可能です。大規模アプリの資産管理や複雑な最適化に向いています。
- 長所:splitChunks、persistent cache、Module Federation によるランタイム共有が可能。
- 短所:設定が複雑になりやすく、最適化には経験が必要。
- 採用検討ポイント:マイクロフロントや高度なランタイム最適化が必須か、既存資産が多いかを確認する。
- 参考: https://webpack.js.org/
Vite の特徴と使いどころ
Vite は開発時にネイティブESMと esbuild を使い起動と HMR を高速化します。プロダクションビルドは内部で Rollup を用いる設計になっています(メジャーバージョン差で挙動が変わるため確認を推奨)。
- 長所:非常に速い dev 体験、プラグインは多くが Rollup 互換。
- 短所:dev と prod のパイプラインが異なるため、プラグインや振る舞いに差が出る場合がある。
- 採用検討ポイント:開発起動・HMR を重視するSPAや中小規模アプリに適する。
- 参考: https://vitejs.dev/
Rollup の強みと使いどころ
Rollup はライブラリビルドで優れたツリーシェイキングを示します。複数フォーマットの出力設定が容易です。
- 長所:クリーンな ESM 出力、優れたツリーシェイキング。
- 短所:単体での開発サーバやHMRは限定的なため、別ツールと組み合わせることが多い。
- 採用検討ポイント:ライブラリの最小化と複数フォーマット出力を重視する場合に最適。
- 参考: https://rollupjs.org/
用語の簡単な定義(初心者向け)
ここで登場する主要概念を簡潔に説明します。
- Module Federation:Webpack の機能で、ランタイムに別ビルドを動的に読み込んでモジュールを共有する仕組みです。依存のバージョン整合や SSR での取り回しに注意が必要です。
- Import Maps:ブラウザ上でモジュール名をURLにマッピングする仕組みで、マイクロフロントの別解として静的に参照を切り替えられます。
- HMR(Hot Module Replacement):変更部分のみを差分適用して再読み込みを最小化する仕組みで、環境依存の設定ミスで動かないケースが多く見られます。
実測ベンチと再現手順(測定条件・コマンド・統計)
再現性のあるベンチを行うために、測定環境・コマンド・統計の取り方を明示します。代表値は環境依存なので必ず手元でPoCを実行してください。
測定環境と前提
サンプルの基準環境(例)を明示します。手元の環境が異なる場合は結果も変わります。
- OS: Ubuntu 22.04 (Linux) / macOS Ventura(どちらか一方で統一して測定)
- CPU: Intel Core i7-12700K(12コア, P-core 3.6GHz)を例にするが、Apple Silicon でも差分を記載すること。
- メモリ: 16–32GB、ストレージ: NVMe SSD 推奨。
- Node: Node 18.x(LTS 系)を基準にする。
- パッケージマネージャ: pnpm v8 / npm 9 / yarn v1/berry のいずれか。
- ベンチツール: hyperfine、Puppeteer(HMR測定)、gzip-size / brotli-size。
- 依存プラグイン一覧: 測定時に使用する主要プラグインはリポジトリに明記する(例: @vitejs/plugin-react, vite-plugin-checker, ts-loader, fork-ts-checker-webpack-plugin, @rollup/plugin-typescript 等)。
測定ハーネスとコマンド(再現手順)
簡易的に再現可能なベンチハーネスの作り方とコマンド例を示します。raw データを JSON で保存してください。
- リポジトリ構成例(任意の実装を3種用意)
-
/apps/vite, /apps/webpack, /apps/rollup
-
依存インストール(pnpm 例)
|
1 2 |
pnpm install --frozen-lockfile |
- 本番ビルド時間測定(hyperfine 例)
|
1 2 3 4 5 |
# Vite build を 7 回実行して JSON 出力 hyperfine --warmup 1 --runs 7 \ "pnpm --filter @app:vite build" \ --export-json bench/vite-build.json |
- 開発サーバ起動(cold)測定——簡易スクリプトを使って "ready" 出力を検出する方法
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// scripts/measure-start.js const { spawn } = require('child_process'); const [,, cmd] = process.argv; const start = Date.now(); const proc = spawn(cmd, { shell: true }); const readyRe = /(ready|Local:|Compiled successfully|dev server running)/i; proc.stdout.on('data', d => { process.stdout.write(d); if (readyRe.test(d.toString())) { console.log('READY_MS', Date.now() - start); proc.kill(); process.exit(0); } }); proc.stderr.on('data', d => process.stderr.write(d)); |
そして hyperfine でラップします。
|
1 2 |
hyperfine --runs 7 "node scripts/measure-start.js 'pnpm --filter @app:vite dev'" --export-json bench/vite-dev.json |
-
HMR 往復測定は Puppeteer や Playwright を用いて、ファイルを保存してからクライアント側で更新イベント(consoleログ)を受け取るまでの時間を計測します。
-
生データ管理:
bench/*.jsonをコミットし、中央値・平均・標準偏差を記載した README を付けること。
サンプル結果(代表値・統計の例)
以下はサンプルリポジトリで得られた代表値の例です。必ずローカルで再現してください。
| ケース | 環境(例) | Vite | Webpack | Rollup |
|---|---|---|---|---|
| 小規模SPA 起動(cold) median (n=7) | i7-12700K / Node18 | 0.18s (mean 0.20, std 0.04) | 3.2s (mean 3.5, std 0.5) | N/A(別サーバ必要) |
| HMR 往復(軽微変更) median (n=50) | 同上 | 60ms (mean 70ms, std 15ms) | 220ms (mean 240ms, std 40ms) | 遅延が大きい場合あり |
| 本番ビルド時間(cold) median (n=5) | 同上 | 9.4s (mean 10.2s, std 1.1s) | 28.5s (mean 30.1s, std 2.8s) | 14.0s (mean 14.6s, std 1.2s) |
注記:上の数値はあくまで参考例です。測定回数(n)、中央値・平均・標準偏差を必ず記録し、raw JSON を保存してください。
プラグイン・アセット・言語サポート(TypeScript/JSX/CSS/画像等)
バンドラー選択時に重要なポイントは、変換と型チェックをどう分離するかです。ここでは主要言語とアセットの扱い方を統合して説明します。
TypeScript の型チェックとトランスフォーム
型チェックとトランスフォームは分離する運用が一般的で再現性が高いです。
- トランスフォーム:Vite は esbuild(または SWC)で高速トランスフォームを行います。Webpack は babel-loader / ts-loader、Rollup は @rollup/plugin-typescript や swc プラグインを利用します。
- 型チェック:CI で
tsc --noEmitを実行して型安全性を確保します。開発中はvite-plugin-checkerやfork-ts-checker-webpack-pluginで補助すると便利です。 - 備考:esbuild や SWC は型情報を消費しないため、型チェックは必ず別コマンドで行ってください。
JSX / React
React の変換は各ツールで主に以下を使います。
- Vite:@vitejs/plugin-react(SWC/ESBuild オプションあり)
- Webpack:babel-loader + @babel/preset-react
- Rollup:@rollup/plugin-babel や swc プラグイン
CSS / CSS Modules / PostCSS
- Webpack:css-loader + MiniCssExtractPlugin で細かな制御が可能です。
- Vite:組み込みで CSS Modules と PostCSS をサポートし、ビルド時に抽出します。
- Rollup:rollup-plugin-postcss を利用。
画像・静的アセット
- Webpack:asset modules により一元管理可能。
- Vite:インポートに基づくハッシュ付き出力と public ディレクトリをサポート。
- Rollup:url / image プラグインで対応。
高度な連携と運用上の考慮点
実運用ではコード分割・マイクロフロント・CI のキャッシュ戦略・セキュリティ対策等が重要です。以下に実務上のポイントと具体例を示します。
コード分割・ロード戦略とプリフェッチ/キャッシュ
チャンクの命名とプリフェッチ戦略を設計してキャッシュ効率を高めます。
- chunkFileNames / manualChunks を設定し、キャッシュキーの安定性を確保します。
- 初期ロードはプリロード、将来的に使うチャンクはプリフェッチを使い分けます。
- CDN と HTTP/2/3 を踏まえ、長期キャッシュ+ファイル名ハッシュでインバリデーションを設計します。
マイクロフロント/Module Federation の実装上の注意
Module Federation はランタイムでモジュールを共有する強力な仕組みですが、制約や互換性問題を把握する必要があります。
- 注意点:依存のバージョン整合、shared 設定の singleton 指定、ランタイムでの解決失敗などのリスクがあります。SSR 対応は追加のランタイム実装が必要になることが多いです。
- 代替手段:Import Maps や CDN を使った外部ロード、iframe 分離などを検討してください。
- Module Federation 例(webpack):
|
1 2 3 4 5 6 7 |
new ModuleFederationPlugin({ name: 'app1', filename: 'remoteEntry.js', exposes: { './Button': './src/components/Button' }, shared: { react: { singleton: true, requiredVersion: '^18.0.0' } } }) |
- 設計時はバージョン解決ルールとフォールバック戦略を明確にしてください。
CI/CD・キャッシュ最適化・CDN配信
CI では依存キャッシュとビルドキャッシュを適切に設計します。
- パッケージマネージャのキャッシュ(pnpm store / npm cache)を活用する。
- ビルドキャッシュ(webpack の persistent cache、Vite のキャッシュ)を保存し、キャッシュキーに lockfile のハッシュと Node バージョンを含める。
- GitHub Actions のキャッシュ例(pnpm store):
|
1 2 3 4 5 |
- uses: actions/cache@v4 with: path: ~/.pnpm-store key: pnpm-${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/pnpm-lock.yaml') }} |
- アーティファクトはステージング用に保存し、差分デプロイやアトミックリリースを利用してCDNの整合性を保つ。
セキュリティ・ライセンス確認の具体手順
脆弱性チェックとライセンス確認は自動化することが重要です。ツールとコマンド例を示します。
- 依存脆弱性チェック(ローカル/CI):
- npm:
npm audit --json > audit.json - pnpm:
pnpm audit --json > audit.json - Snyk:
npx snyk test --json > snyk.json - ライセンス確認:
- license-checker:
npx license-checker --production --json > licenses.json - 重大なライセンス(例: copyleft)を自動検出してブロックするルールをCIに入れる。
- CI での例(簡易):
|
1 2 3 4 5 6 |
- name: npm audit run: npm audit --json > audit.json - name: Snyk test run: npx snyk test --severity-threshold=high |
- 備考:Snyk 等は商用機能があるため権限・トークン管理に注意してください。
移行ハンズオンと選定サポート(テンプレート/PoC)
移行は段階的に行うとリスクを下げられます。ここでは必須パッケージ、インストールコマンド、CI 設定例、よくある落とし穴と回避法を示します。
Webpack → Vite の移行手順
移行を安全に進めるための代表的なステップとコマンド例を示します。
- 現行ビルド設定の監査:entry/output、loader/pluginの一覧を作る。
- 必須機能のマッピング:各 loader/plugin を Vite の代替にマッピングする。
- Alias とモジュール解決の移行:resolve.alias を移す。
- 環境変数の差分:process.env → import.meta.env にマイグレーション。
- Static assets/public の取り扱いを確認する。
- TypeScript 型チェック:esbuild は型チェックをしないため CI に
tsc --noEmitを追加する。 - HMR とプラグイン動作の確認:dev と build で挙動差があるプラグインは要テスト。
- CI の更新:pnpm install→build コマンドを置き換え、キャッシュキーを更新する。
- 本番ビルドの比較:サイズ・ソースマップ・ツリーシェイキングを評価する。
- ロールアウトとフォールバック:段階的切替と迅速なロールバックプランを用意する。
インストールの具体例(React + TypeScript の最小セット、例: Vite 4 系を想定):
|
1 2 3 |
pnpm add -D vite@^4 @vitejs/plugin-react typescript@^4.9 vite-plugin-checker pnpm add react react-dom |
package.json の scripts 例:
|
1 2 3 4 5 6 7 |
"scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview", "type-check": "tsc --noEmit" } |
CI キャッシュ例と注意点は前述の通りです。CommonJS の挙動差や動的 require に注意し、optimizeDeps の設定で事前バンドルすることを検討してください。
Webpack → Rollup の移行手順(ライブラリ向け)
ライブラリを Rollup に移す代表的手順です。
- エントリを整理し公開APIを1つにまとめる。
- external 設定で peerDependencies(react等)を明示する。
- プラグインを Rollup 用に置換する(@rollup/plugin-typescript 等)。
- 出力フォーマット(esm/cjs/umd)を必要に応じて追加する。
- sourcemap と型定義(d.ts)生成を確認する。
- 複数フォーマットビルドとアーティファクト保存をCIで実装する。
インストール例:
|
1 2 |
pnpm add -D rollup @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-typescript rollup-plugin-terser |
設定テンプレート(最小構成:dev / prod)
Vite(React + TypeScript)の最小構成例(コメントは省略):
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [react()], resolve: { alias: [{ find: '@', replacement: '/src' }] }, build: { sourcemap: true, rollupOptions: { output: { entryFileNames: 'assets/[name].[hash].js', chunkFileNames: 'assets/[name].[hash].js', assetFileNames: 'assets/[name].[hash].[ext]' } } } }) |
Webpack(production 抽出部分の最小例)と Rollup(ライブラリ向け最小)については、既存のテンプレートをベースにプラグインの推奨バージョンを README に明記してください。テンプレートには必ず「推奨バージョン」や「既知の互換問題」を注記すること(例: esbuild による CJS 変換の注意点)。
PoC手順と評価テンプレート(必須検証項目)
PoC で必ず評価する項目を定義します。各項目は 1–5 で評価し、重み付け合算して比較してください。
- dev 起動時間(cold, median)
- HMR 往復時間(軽微・大変更)
- 本番ビルド時間(cold)
- 出力サイズ(raw / gzip / brotli)
- ツリーシェイキング効果(未使用削除率)
- ソースマップの品質と生成時間
- 型チェックの取り回し(CI)
- 必要プラグインの互換性と機能差
- CI のキャッシュ適用度
測定は前述の hyperfine / Puppeteer ベンチハーネスで自動化し、結果(median/mean/stddev/raw JSON)を保存して比較します。
選定フロー(簡易判定)
条件に応じた簡易的な判断フローを示します。詳細はプロジェクト要件に合わせて調整してください。
- ライブラリで ESM 重視 → Rollup を第一候補。
- 大規模モノリポかつマイクロフロント要件 → Webpack(Module Federation)を検討。
- 開発スピードと HMR を最重視 → Vite を候補。
- 既存 Webpack 資産が多くカスタム最適化が必要 → Webpack(移行コストを見積もる)。
よくあるトラブルシューティングとFAQ
実務で遭遇しやすい障害とログ例、対処法を示します。ログは状況を把握するための具体例です。
HMR が効かない
原因としてサーバ側・クライアント側のキャッシュ、プラグイン干渉、ポートやプロキシ設定があります。
- 代表的ログ(ブラウザコンソール)
|
1 2 |
[vite] server connection lost. reconnecting... |
- 対処:ブラウザキャッシュ削除、プロキシ経路の確認、プラグインを一つずつ無効化して切り分け。
ビルドで "Unexpected token 'export'" のエラー
CommonJS と ESM の混在で起きることがあります。
- 代表的ログ(ブラウザ / ビルド)
|
1 2 |
Uncaught SyntaxError: Unexpected token 'export' |
- 対処:問題のモジュールを事前に ESM に変換するか、optimizeDeps/alias で CJS をビルド時に解決する。esbuild の CJS 変換に制約があるため注意。
Module Federation の共有衝突
shared 設定のミスマッチでランタイム例外が発生することがあります。
- 代表的ログ
|
1 2 |
Uncaught (in promise) Error: Shared module version mismatch for "react" |
- 対処:shared で requiredVersion を揃え、singleton を必要に応じて指定する。SSR を使う場合はサーバ側での解決ロジックを追加する。
まとめ:バンドラー選定の要点
要件定義とKPIを最初に決め、PoCで必ず定量評価してください。開発体験を最重視するなら Vite が有力候補になりやすく、ライブラリは Rollup がツリーシェイキングとフォーマット出力で有利です。マイクロフロントや高度なランタイム最適化が必要な場合は Webpack の柔軟性を評価してください。提示した測定ハーネスとテンプレートを用いて、中央値・標準偏差・raw データを保存し、チームで合意できる形で採用判断を行ってください。