Bundler は、複数のJavaScriptモジュールを1つまたは複数のファイルに結合し、ブラウザやモバイルアプリケーションで使用するためのビルドツールです。最新のバンドラー(Webpack、Metro、Vite)はファイルを結合するだけでなく、トランスパイル、ミニフィケーション、リソース最適化も実行します。Webpack Concepts(2026)によると、適切なバンドラーの設定により、機能を損なうことなくビルドサイズを40~60%削減できます。
重要なポイント
Bundler(モジュールバンドラー)は、エントリーポイント(アプリケーションのメインファイル)を受け取り、そのすべての依存関係を再帰的にたどってモジュールグラフを構築するコマンドラインツールです。出力として、バンドラーはHTMLページに含めたり、モバイルアプリケーションで実行したりできる1つ以上のファイルを生成します。
長い間、ブラウザはプラットフォームレベルでJavaScriptモジュールシステムをサポートしていませんでした。Bundler は、数百のimportとrequireを1つのファイルに変換することでこの問題を解決しました。現在では、ブラウザがネイティブでESモジュールをサポートしているにもかかわらず、バンドラーは追加のタスクを実行します:JSXとTypeScriptのトランスパイル、コードのミニフィケーション、遅延読み込みのためのコード分割、開発を高速化するためのホットモジュールリプレースメント(HMR)。
モバイル開発では、React NativeはデフォルトのバンドラーとしてMetroを使用し、FlutterはDartベースの独自のビルドシステムを使用します。バンドラーの選択は、開発速度、アプリケーションサイズ、ランタイムパフォーマンスに直接影響します。
バンドリング は、入力ファイルの解析、依存関係グラフの構築、モジュールの変換、出力ファイルの生成のいくつかの段階を経ます。各段階で、バンドラーはソースコードを変換するローダーと最適化のためのプラグインを適用します。
バンドラーはエントリーポイントから開始し、ファイルを読み取り、AST(抽象構文木)を構築します。ASTからすべてのimportとrequire文が抽出されます。見つかったインポートごとに、バンドラーはすべての依存関係が1つのグラフに収集されるまでプロセスを再帰的に繰り返します。
// バンドラーの簡略化された実装
const fs = require('fs');
const path = require('path');
function buildGraph(entry) {
const content = fs.readFileSync(entry, 'utf-8');
const imports = content.match(/require\(['"](.+?)['"]\)/g);
const modulePath = path.resolve(path.dirname(entry), imports[0]);
return { entry, content, deps: [buildGraph(modulePath)] };
}
グラフを構築した後、バンドラーはローダー(ファイルをJavaScriptに変換する変換:TypeScript → JS、SCSS → CSS、JSX → JSX関数)を適用します。次にプラグインが追加の変換(ミニフィケーション、画像のインライン化、サービスワーカーの生成)を実行します。
バンドラー は、従来型、特化型、ESモジュールベースの次世代型の3世代に分類されます。各タイプには独自のアーキテクチャと適用分野があります。
Webpack は巨大なエコシステムを持つ最も普及しているバンドラーです。その主な利点は柔軟性です:何千ものローダーとプラグインがあらゆるビルドシナリオをカバーします。Parcel はゼロコンフィグアプローチを提供し、ファイル拡張子に基づいて必要な変換を自動的に検出します。両方のバンドラーがコード分割とHMRをサポートしています。
Metro はReact Native専用に作られ、JavaScriptとTypeScriptでのみ動作します。そのアーキテクチャはモバイル開発向けに最適化されています:インラインrequire、非同期モジュールローディング、Hermesエンジンとの統合をサポート。Rollup はライブラリとnpmパッケージに焦点を当てており、不要なラッパーなしでクリーンなESモジュール出力を生成します。
バンドラーの選択は、プラットフォームとプロジェクトの要件によって異なります。Webpack は汎用的ですが、詳細な設定が必要です。Metro はReact Nativeの唯一の選択肢です。Vite はネイティブESモジュールのおかげで最速の開発体験を提供します。
| 特徴 | Webpack | Metro | Vite |
|---|---|---|---|
| プラットフォーム | Web、汎用 | React Native | Web、汎用 |
| 開発ビルド速度 | 中程度 | 高速 | 非常に高速 |
| コード分割 | あり | あり | あり |
| HMR | あり | Fast Refresh | 瞬時HMR |
| プラグインエコシステム | 巨大 | 限定的 | 成長中 |
| 設定 | 複雑 | シンプル | シンプル |
| TypeScriptサポート | ローダー経由 | 内蔵 | 内蔵 |
Vite は依存関係の事前バンドリングにesbuild、プロダクションビルドにRollupを使用し、開発モードでWebpackと比較して5~10倍の速度向上を実現します。Metro はWebターゲットをサポートしていませんが、React NativeブリッジおよびTurbo Modulesとのシームレスな統合を提供します。
設定 により、さまざまなファイルタイプの処理方法、ビルドの生成場所、適用する最適化が決まります。一般的な設定ファイルには、エントリーポイント、モジュールルール、プラグイン、出力設定が含まれます。
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js',
clean: true,
},
module: {
rules: [
{
test: /\.(js|jsx|ts|tsx)$/,
exclude: /node_modules/,
use: { loader: 'babel-loader', options: { presets: ['@babel/preset-react'] } },
},
{ test: /\.css$/, use: ['style-loader', 'css-loader'] },
],
},
plugins: [new HtmlWebpackPlugin({ template: './public/index.html' })],
devServer: { port: 3000, hot: true },
};
Metro は設定に別々のファイルmetro.config.jsとbabel.config.jsを使用します。Webpackとは異なり、Metroの設定ははるかにシンプルです。バンドラーがReact Nativeに特化しており、スタイルや画像のローダーを設定する必要がないからです。
経験豊富な開発者でもバンドリングエラーに遭遇します。Duplicate module は最も一般的な問題の1つで、package.jsonのバージョンの非互換性により、同じ依存関係がバンドルに複数回含まれます。Module not found は、インポートパスが間違っているか、パッケージがインストールされていない場合に発生します。
Metro はモバイルデバイスのバンドルサイズに制限があります。2~5MBの制限を超えると、デバイスのメモリ不足によりUnable to resolve moduleエラーが発生します。解決策は、inline requiresとRAM bundlesを使用してモジュールを遅延読み込みすることです。Metroはシンボリックリンクにも敏感なため、モノレポでは設定に追加のwatchFoldersが必要です。
Webpack の場合、典型的な問題はModule parse failedで、ファイルの形式が予期しないものか、必要なローダーがない場合に発生します。ファイル拡張子を確認し、module.rulesに適切なルールを追加することで解決します。source-map-explorerを使用すると、最終的なバンドル内の重複モジュールや冗長な依存関係を見つけるのに役立ちます。
ビルドの遅さは、バンドラーを扱う際の主要な問題の1つです。高速化するには、実行間でモジュール変換結果を保存する永続キャッシュを使用します。Webpack 5はcache: { type: 'filesystem' }によるファイルキャッシュをサポートし、Viteは事前バンドリングにesbuildを使用し、Metroはメモリ内モジュールキャッシュに依存します。さらに、ローダーでnode_modulesにexcludeを指定して、すでにビルド済みのパッケージを再処理しないようにします。
コード分割 は、バンドルをオンデマンドでロードされるチャンクに分割します。バンドラーごとにこの戦略の実装方法が異なります。Webpackは動的インポートimport()をサポートし、モジュールごとに個別のチャンクを作成します。Metroは遅延読み込みにインラインrequireを使用します。Viteは追加設定なしでベンダーチャンクと動的インポートを自動的に分割します。戦略の選択はプロジェクトの要件によって異なります:モバイルアプリケーションの場合は、遅延読み込みによる小さな初期バンドルが推奨されます。
React Nativeでコード分割を正しく機能させるには、追加の設定が必要です:MetroをinlineRequiresを介して非同期チャンク用に設定する必要があります。WebpackとViteでは、動的import()構文を使用するとコード分割がそのまま機能し、自動的に分割ポイントが作成されます。最大の効率を得るには、重要なチャンクに<link rel="preload">を介したプリロードヒントとコード分割を組み合わせます。
よくある質問
JSX、TypeScript、CSSモジュールを使用しないシンプルなプロジェクトでは、ブラウザのネイティブESモジュールで済ませられます。ただし、プロダクションビルドでは、バンドラーがミニフィケーション、ツリーシェイキング、コード分割を提供し、これらはパフォーマンスに重要です。バンドラーなしでは、大規模プロジェクトでモジュラーアーキテクチャを維持するのは困難です。
React Native の場合 — Metro(唯一サポートされているオプション)。Webアプリケーションの場合 — 新規プロジェクトにはVite(速度のため)、既存プロジェクトにはWebpack(豊富なエコシステム)。ライブラリやnpmパッケージの場合 — Rollup(クリーンなESモジュール出力を生成するため)。
バンドラー は最終的なJSバンドルのサイズを直接決定します。ツリーシェイキングを使用したWebpackはサイズを30~50%削減できます。React Native向けのMetroはHermesバイトコードをサポートしており、通常のJavaScriptと比較してサイズを20~30%削減します。コード分割によりモジュールをオンデマンドでロードでき、初期バンドルを削減します。
Webの場合、バンドラー はさまざまなブラウザと互換性のあるコードを生成し、CSS、画像、フォントをサポートします。モバイルプラットフォームの場合、Metro はJavaScriptエンジン(HermesまたはJSC)用のコードを生成し、CSSやHTMLを処理せず、デバイスの限られたリソースに合わせてビルドを最適化します。
はい、一部のプロジェクトではWeb部分にWebpack、React NativeにMetroを使用しています(例えば、共有コードベースのモノレポ)。ただし、これによりCI/CDの設定が複雑になり、依存関係のバージョン同期が必要になります。可能であれば、すべてのターゲットに単一のバンドラーを使用することをお勧めします。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。