JavaScript | ES6+ 文法:モジュール – バンドラとの関係

JavaScript JavaScript
スポンサーリンク

そもそも「バンドラ」とは何か(まずイメージから)

バンドラ(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>
HTML

main.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.js 1ファイルにまとまる

ネットワークリクエストが減り、読み込みが高速になります。

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 コードの無駄な空白・改行・コメントを削り、
変数名も短くして(例:counta)ファイルサイズを小さくします。

これは 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));
JavaScript

2. バンドラの設定で「エントリーポイント」を指定する

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 が、バンドラ経由だとどう振る舞うのか」が体感できます。
そこから先は、設計(モジュール分割)とツール(バンドラ)の両方が、自然と繋がって見えてきます。

タイトルとURLをコピーしました