概要
JavaScriptのモジュール構文は、ただの構文選択にとどまらない。
それは**“コードの依存構造、分割単位、実行タイミングを制御し、プロジェクト全体の設計哲学を反映するアーキテクチャ戦略”**である。
ESModules(ESM)とCommonJS(CJS)は互換性がありそうで実は根本的に異なる。
同期 vs 非同期、評価タイミング、ファイル分割の哲学、環境ごとの挙動差。
それらを踏まえた上で、モジュールの選定・設計・切り替え判断を明確にする必要がある。
本稿では、ESM/CJS の構造的違い・適用環境・設計上の注意点・ハイブリッド戦略を網羅的に解説する。
1. 基本構文の比較
ESModules(ESM)
// add.js
export function add(a, b) {
return a + b;
}
// index.js
import { add } from './add.js';
- ✅ 静的構文(ツール・解析・最適化に強い)
- ✅ トップレベル await など最新機能に対応
CommonJS(CJS)
// add.js
function add(a, b) {
return a + b;
}
module.exports = add;
// index.js
const add = require('./add');
- ✅ 同期読み込み(Node.jsの標準構造)
- ✅ ファイル単位で実行時に評価される
2. 実行タイミングの違い
特性 | ESModules | CommonJS |
---|---|---|
ロード方法 | 非同期(事前に解決) | 同期(即時評価) |
import/export の解釈 | 静的解析 | 動的(requireで評価) |
実行タイミング | ロード後に評価 | 読み込み時に即時評価 |
- ✅ ESMは**事前解決(preloading)**により、依存関係の最適化が可能
- ✅ CJSは実行時の柔軟性に長けるが、最適化しづらい
3. 環境による選定ポイント
使用環境 | 推奨モジュール方式 |
---|---|
フロントエンド | ✅ ESModules(標準) |
Node.js (新規) | ✅ ESModules(推奨) |
Node.js (旧構成) | ✅ CommonJS(互換維持) |
ライブラリ開発 | ✅ 両対応 or dual export |
4. ハイブリッド設計とDual Export戦略
// package.json
{
"type": "module", // ← ESMとして解釈される
"exports": {
".": {
"require": "./index.cjs",
"import": "./index.mjs"
}
}
}
- ✅ ライブラリとして公開する際はESMとCJSの両対応がベター
- ✅
exports
フィールドでエントリーポイントを切り分ける
5. import/export の設計的ベストプラクティス
- ✅ named export を優先する(ツリーシェイキング可能)
- ✅ default export は構造の意味が曖昧になりがち
- ✅ ルートレベルで import すること(ネスト import は避ける)
// ❌ import のネスト
function load() {
import('./module.js').then(mod => mod.init());
}
// ✅ 依存は事前に宣言する
import { init } from './module.js';
設計判断フロー
① 実行環境はどこか?(Node / ブラウザ / 両方)
② ロードタイミングと構文の柔軟性どちらを優先すべきか?
③ モジュールの分割粒度と依存管理方針は定まっているか?
④ 今後ESMに移行する可能性があるか? → デフォルトはESMで構築
⑤ ライブラリとして配布する? → dual export戦略を選定
よくあるミスと対策
❌ CJSで import
を使いSyntaxError
→ ✅ type: "module"
が設定されていない or 拡張子が .js
のまま
❌ require
と import
を混在して不可解な挙動
→ ✅ 構文と評価タイミングが異なるため、統一 or 分離すべき
❌ default export に過度依存し、意味の不明瞭な構造に
→ ✅ 明示的な名前付き export に切り替え、構造を明確に
結語
モジュール構文は「ファイルを分けるため」ではない。
それは**“依存と責任の境界を設計し、実行環境に最適化された構造を提供するための構成戦略”**である。
- ESMは構造の静的最適化とモダン仕様に強い
- CJSはレガシー互換性と即時評価に優れる
- 最適解は「目的に応じた構文の選定と明示的設計」にある
JavaScriptにおけるモジュール設計とは、
“依存を構造として管理することで、拡張と保守を支える実行戦略”である。