そもそも「バンドラ」とは何か(まずイメージから)
バンドラ(bundler)は、
「たくさんの JS ファイル(モジュール)を、ブラウザが読みやすい形にまとめてくれる道具」 です。
ES6 の import / export を使うと、ファイルをどんどん分割できますが、
ブラウザにそのまま読み込ませると、たくさんの <script> やネットワークリクエストが必要になります。
そこでバンドラが登場します。
バンドラの役割をざっくり言うと:
- たくさんのモジュール(.js ファイル)を読み込む
- 依存関係を解析する(どのファイルがどれを import しているか)
- それらをまとめて、1つまたはいくつかの「最適化された JS ファイル」に変換する
代表的なバンドラとして、webpack / Rollup / Parcel / Vite などがあります。
(Vite は開発中はネイティブ ES モジュール、ビルド時に Rollup でバンドル、というハイブリッド型)
ここが重要です。
ES モジュールは “設計のための仕組み”、バンドラは “配布用にまとめるための道具”
という役割分担だと捉えると、関係が見えやすくなります。
生の ES モジュール vs バンドラ経由のモジュール
ブラウザのネイティブ ES モジュール(type=”module”)
ブラウザは今、ES モジュールをネイティブに理解できます。
<script type="module" src="./main.js"></script>
HTMLmain.js の中で:
import { add } from "./math.js";
JavaScriptと書けば、ブラウザが math.js も追加で取りに行きます。
このやり方のメリットは:
- 仕組みがシンプル:ブラウザと直接やり取りするだけ
- 小さなプロジェクトならこれだけで十分
一方で、大きなアプリになってくると:
- モジュールファイルの数が増え、HTTP リクエストが多くなる
- 古いブラウザ向けの変換(トランスパイル)が必要
- 画像や CSS、TypeScript なども一緒に扱いたくなる
といった事情が出てきて、「ビルド工程」が欲しくなります。
そこでバンドラの出番です。
バンドラを通すと何が変わるか
バンドラを使うと、開発中は普通に import / export を書きます。
// main.js
import { add } from "./math.js";
console.log(add(2, 3));
JavaScriptビルドすると、バンドラがすべての依存関係を解析して、
それらを 1 つの bundle.js にまとめてくれます。
<script src="./bundle.js"></script>
HTMLという形で配信できるようになります。
ここが重要です。
あなたは「モジュールを分割して綺麗に設計する」ことに集中でき、
実際のブラウザへの配布や最適化はバンドラが面倒を見てくれる。
ES モジュールとバンドラは、まさに役割分担の関係です。
バンドラがやってくれる主なこと
依存関係の解析と 1 ファイル(または数ファイル)への結合
バンドラは、メインエントリ(例:src/main.js)からスタートし、
そこから辿れるすべての import をたどって「依存グラフ」を作ります。
そして、そのグラフに含まれるコードを、
1つまたは複数のファイルにまとめて出力します。
例:
- 開発時:
src/main.js,src/math.js,src/user.js, … などを分割 - 本番用:
dist/bundle.js1ファイルにまとまる
ネットワークリクエストが減り、読み込みが高速になります。
Tree Shaking(使っていないコードを削る)
ES モジュールの import / export は静的解析しやすいため、
バンドラは「どの関数・クラスが実際に使われているか」を判断できます。
// util.js
export function used() { /* 使われている */ }
export function unused() { /* どこからも呼ばれていない */ }
JavaScript// main.js
import { used } from "./util.js";
used();
JavaScriptこの場合、バンドラは unused がどこからも import / 使用されていないことを検出して、
本番ビルドでは unused のコードを削除できます。
ここが重要です。
ES モジュール+バンドラ ≒ 「使っているところだけを本番コードに残せる」。
この仕組みを Tree Shaking と呼びます。
モジュールを綺麗に分割するほど、Tree Shaking の効果も出やすくなります。
コードの最小化(minify)・圧縮
バンドラは、最終的な JS コードの無駄な空白・改行・コメントを削り、
変数名も短くして(例:count → a)ファイルサイズを小さくします。
これは ES モジュールとは直接関係ないですが、
「モジュールをまとめる過程」で一緒にやってくれる処理です。
ES モジュールで設計しやすく、バンドラで配布しやすく、
両方が揃って初めて、現代的なフロントエンド開発が成り立っています。
コード分割(動的 import を利用した遅延読み込み)
ES モジュールの動的 import(import("./xxx.js"))と組み合わせると、
バンドラは「ここは分割できるポイントだ」と判断し、
別ファイル(チャンク)として切り出してくれます。
// main.js
button.addEventListener("click", async () => {
const module = await import("./heavy.js");
module.runHeavyTask();
});
JavaScriptこう書くと、バンドラは
main.jsを含む初期バンドルheavy.jsを含む別バンドル(必要になったときだけ読み込む)
のように、自動的に分割してくれます。
ここが重要です。
「ES モジュールの文法(動的 import)」が、「バンドラにとってのヒント(ここで分割してよい)」になる。
文法とツールが連携しているイメージを持つと理解しやすいです。
初心者が知っておくと楽になる「現代的な構成の流れ」
1. コードは普通に ES モジュールで書く
まず、開発段階では単純に import / export を使うだけでOKです。
// src/math.js
export function add(a, b) {
return a + b;
}
JavaScript// src/main.js
import { add } from "./math.js";
console.log(add(2, 3));
JavaScript2. バンドラの設定で「エントリーポイント」を指定する
webpack や Vite などを使うとき、
「このファイルからアプリが始まる」という入口(エントリーポイント)を設定します。
例:Vite なら index.html / main.js を見つけて勝手にやってくれます。
webpack なら entry: "./src/main.js" のように指定します。
バンドラはそこから依存関係をたどり、必要なモジュールを全部まとめます。
3. 開発中は開発サーバー、本番用にビルド
多くのバンドラは「開発用」と「本番ビルド用」の両方を提供しています。
開発時:
- ES モジュールをそのまま扱ったり
- 変更を自動リロードしたり
本番ビルド時:
- すべてのモジュールをまとめて
- 不要なコードを削除し
- minify して
最終的な dist/ ディレクトリを生成します。
あなたは、常に ES モジュールとして設計し、
「配布形態」はバンドラに任せる、という流れです。
バンドラを意識したモジュール設計のコツ
「モジュールを小さく、依存を片方向に」にする
これはバンドラというより「設計」そのものですが、
モジュールが小さく保たれ、依存が一方向に揃っていると:
- Tree Shaking が効きやすい
- 循環依存によるトラブルが減る
- コード分割ポイントを見つけやすくなる
など、バンドラにとっても非常に扱いやすくなります。
外部ライブラリはどう扱われるかを知っておくと便利
例えば、import _ from "lodash"; のように、
npm からインストールしたライブラリを import すると、
バンドラはそれも含めて bundle に入れてくれます。
一方で、「CDN から <script> で別途読み込むから、bundle には含めたくない」という場合は、externals 設定(webpack)や external 設定(Rollup)などで除外できます。
これは少し進んだ話ですが、
「バンドラは npm の依存も一緒にまとめる」
という感覚だけ持っておくと、後で混乱しにくくなります。
path の書き方(相対パス / 絶対パス / エイリアス)
バンドラは、import "./xxx.js" のような相対パスだけでなく、import "@/models/User" のような「エイリアス」も解決できるよう設定できます。
エイリアスとは、特定のフォルダへのショートカットです。
例えば:
@/ → src/ を指すように設定しておけば、
import User from "@/models/User.js";
JavaScriptのように書けます。
ここが重要です。
ES モジュールの世界では、本来は相対パスが基本。
バンドラを入れると、より便利なモジュール解決(エイリアスなど)が使えるようになる。
「なぜ教科書では全部 ./ なのに、現場コードは @/ とか書いてあるの?」という疑問はここで繋がります。
例:Vite+ES モジュールの超ざっくり流れ
イメージとして、Vite のようなモダンツールだと流れはこんな感じです。
開発中:
src/main.jsなどを<script type="module">としてブラウザに提供- ブラウザが ES モジュール(import/export)を理解して、必要なファイルを取ってくる
- Vite はその途中で変換(TypeScript → JS など)だけ手伝う
本番ビルド時:
- すべてのモジュールを解析し、Rollup でバンドル
- 複数のチャンクに分割(動的 import に応じて)
- Tree Shaking と minify を実施
あなたが書くコードはずっと ES モジュールのまま。
「開発中はネイティブモジュールっぽく動き、本番では最適化された bundle に変わる」
という感じです。
まとめ
「ES6 モジュール」と「バンドラ」の関係を一言でいうと、
ES モジュールは「コードをどう分割して設計するか」の仕組み。
バンドラは「それらをどうまとめて配布するか」の仕組み。
です。
押さえておきたいポイントは:
ES モジュールで、ファイルを役割ごとに分割し、import / export で繋ぐ
ブラウザは今、ネイティブ ES モジュールを理解できるが、大規模になるとバンドラがほぼ必須
バンドラは依存解析・結合・Tree Shaking・minify・コード分割などを自動でやってくれる
動的 import は「コード分割ポイント」としてバンドラに伝わる
あなたは「モジュールとしてきれいに書く」ことに集中し、バンドラに「配布の最適化」を任せればよい
まずは、純粋な ES モジュールだけで小さなアプリを書いてみて、
そのあと Vite や webpack の最小構成を触ってみると、
「同じ import / export が、バンドラ経由だとどう振る舞うのか」が体感できます。
そこから先は、設計(モジュール分割)とツール(バンドラ)の両方が、自然と繋がって見えてきます。

