第8章: モジュールシステム (ES Modules と CommonJS)

第8章: モジュールシステム (ES Modules と CommonJS)

概要

JavaScriptにおけるモジュールとは、コードをファイル単位などで分割し、必要な部分をインポート(import)・エクスポート(export)できる仕組みです。ES6ではES Modules (ESM)と呼ばれる標準モジュールシステムが導入され、import/export文が使えるようになりました。それ以前から、Node.jsではrequire関数とmodule.exportsを使うCommonJSと呼ばれるモジュールシステムが使われており、現在も双方が存在しています。

ES Modulesではimport/export構文によりモジュール間で関数や変数をやり取りします。一方、CommonJSではrequire()でモジュールを読み込み、module.exportsで外部に公開します。技術的な違いとして、CommonJSはモジュールを同期的にロードしますが、ESMでは基本的に非同期にモジュールを読み込みます。またブラウザではESMがネイティブにサポートされているのに対し、CommonJSモジュールをそのまま読み込むことはできず、Webpackなどのバンドラを使って束ねる必要があります。

Node.js環境でも近年はESMがサポートされており、.mjs拡張子やpackage.json"type": "module"を指定することでimport/exportが使えます。新規プロジェクトではES Modulesの使用が推奨されており、既存のCommonJSとの相互運用も可能ですが、モジュールの種類を混在させる際には注意が必要です。

コードと解説

まずES Modulesの例として、モジュールを二つ用意し、一方で変数や関数をexportし、他方でimportして利用します(ブラウザ環境で実行する場合、<script type="module">タグ内で動作します)。

/*** utils.js - モジュールファイル ***/
export const pi = 3.14159;
export function circleArea(radius) {
    return pi * radius * radius;
}

/*** main.js - エントリポイント ***/
import { pi, circleArea } from "./utils.js";

console.log(pi);                // 3.14159
console.log(circleArea(5));     // 78.53975

// デフォルトエクスポートの例
// utils.js 側で: export default function greet() { ... }
// main.js 側で: import greet from "./utils.js";

次に、Node.jsで従来使われていたCommonJSの書き方の例を示します。requiremodule.exportsを用いて同様の機能を実現しています。

/*** utils.cjs ***/
const pi = 3.14159;
function circleArea(radius) {
    return pi * radius * radius;
}
// 複数の値をモジュールの外に公開
module.exports = { pi, circleArea };

/*** main.cjs ***/
const { pi, circleArea } = require("./utils.cjs");

console.log(pi);            // 3.14159
console.log(circleArea(5)); // 78.53975

解説: ES Modules形式では、utils.jsexportされた定数piや関数circleAreaを、main.js側でimportによって読み込んで使用しています。このとき、読み込む側ではimport { 名前 }で対応する名前を指定します。デフォルトエクスポートを使った場合(例ではコメントで記載)、import 任意の名前 from "..."という書式で読み込みます。ES Modulesではこれらimport文はファイル先頭で静的に記述され、モジュールを非同期にロードします。

CommonJS形式では、utils.cjs側でmodule.exports = { pi, circleArea };とまとめてエクスポートし、main.cjs側でrequire("./utils.cjs")により同期的にモジュールをロードしています。CommonJSのrequireは関数呼び出しなので任意の場所で実行できる一方、ES Modulesのimportはファイルロード時に行われる点が異なります。また、上記の例では拡張子.cjsを使用していますが、Node.jsでESMを使う場合には.mjs拡張子にするか、package.jsonでモジュールタイプを指定する必要があります。

まとめると、

現在では多くの場合ES Modulesを使う方向に統一されつつありますが、Node.jsの既存環境や一部ツールではCommonJSが根強く残っています。両者の違いを理解しつつ、必要に応じて変換や互換性設定を行うことで、モダンなモジュール構成で開発を進めることができます。

目次に戻る