CommonJS と ES Module の違い

JavaScript 開発において、モジュール化システムは重要な役割を果たしており、コードの管理と整理に役立ちます。CommonJS と ES Module は主要なモジュール化標準であり、モジュールの読み込み、エクスポートメカニズム、互換性などに顕著な違いがあります。この記事では、これらのモジュールシステムの主な特徴と違いを詳細に比較します。

1. モジュールの読み込み方法

CommonJS:

  • 同期読み込み:CommonJS は同期的にモジュールを読み込みます。つまり、require() 関数を使用すると、モジュールが即座に読み込まれて実行されます。この方法はサーバーサイド(例えば Node.js)には適していますが、ブラウザ環境ではパフォーマンスの問題を引き起こす可能性があります。ブラウザはモジュールの読み込みが完了するまで後続のコードを実行できません。

    1
    // モジュールのインポート
    2
    const math = require("./math");
    3
    console.log(math.add(2, 3));

ES Module:

  • 静的および非同期読み込み:ES Module は静的および非同期の読み込みをサポートしています。モジュールのインポートはコンパイル時に静的に解析され、これにより JavaScript エンジンが最適化を行うことができます。ES Module は import 構文を使用して非同期読み込み(動的 import() を使用)もサポートし、ブラウザ環境では特に重要です。

    1
    // 静的インポート
    2
    import { add } from "./math.js";
    3
    console.log(add(2, 3));
    4
    5
    // 動的インポート
    6
    import("./math.js").then((module) => {
    7
    console.log(module.add(2, 3));
    8
    });

2. モジュールのエクスポートとインポート

CommonJS:

  • モジュールのエクスポートmodule.exports または exports オブジェクトを使用して、モジュールの機能をエクスポートします。オブジェクト、関数、または他の値をエクスポートできます。

    1
    // モジュールのエクスポート
    2
    module.exports = {
    3
    add: function (a, b) {
    4
    return a + b;
    5
    },
    6
    };
  • モジュールのインポートrequire() 関数を使用してモジュールをインポートします。

    1
    // モジュールのインポート
    2
    const math = require("./math");
    3
    console.log(math.add(2, 3));

ES Module:

  • モジュールのエクスポートexport キーワードを使用して、モジュールの機能をエクスポートします。名前付きエクスポートとデフォルトエクスポートがサポートされています。

    1
    // 名前付きエクスポート
    2
    export function add(a, b) {
    3
    return a + b;
    4
    }
    5
    6
    // デフォルトエクスポート
    7
    export default function add(a, b) {
    8
    return a + b;
    9
    }
  • モジュールのインポートimport キーワードを使用してモジュールをインポートします。モジュールの一部または全体をインポートすることができます。

    1
    // 名前付きエクスポートのインポート
    2
    import { add } from "./math.js";
    3
    console.log(add(2, 3));
    4
    5
    // デフォルトエクスポートのインポート
    6
    import add from "./math.js";
    7
    console.log(add(2, 3));

3. モジュールの解決

CommonJS:

  • 動的解決:モジュールのパスは動的に解決され、コードの実行時にモジュールのパスを計算することができます。これにより、条件付きインポートや動的なモジュール読み込みが可能になります。

    1
    const math = require("./math-" + someCondition + ".js");

ES Module:

  • 静的解決:モジュールのパスはコンパイル時に静的に解決され、コンパイラはコードの実行前に依存関係を確定することができます。これにより、静的解析と最適化が可能になりますが、動的なパス解決はサポートされていません。

    1
    import { add } from "./math.js";

4. 互換性

CommonJS:

  • 主に Node.js で使用:CommonJS は Node.js のデフォルトモジュールシステムであり、ブラウザ環境では直接サポートされていませんが、Browserify や Webpack などのツールを使って CommonJS モジュールを使用することができます。

ES Module:

  • 標準化:ES Module は ECMAScript 標準の一部であり、現代のブラウザと Node.js で広くサポートされています。フロントエンドとバックエンドの両方で推奨されるモジュールシステムです。

5. 実行時の挙動

CommonJS:

  • 動的読み込みとキャッシュ:CommonJS モジュールは、最初の require の際に読み込まれて実行され、その後キャッシュされます。同じモジュールへの後続の require 呼び出しはキャッシュ内のモジュールインスタンスを返します。

ES Module:

  • 静的読み込みと循環依存:ES Module は静的解析をサポートし、モジュールの依存関係をコンパイル時に確定することができます。循環依存には対応しており、モジュールが読み込まれる際に他のモジュールの一部内容を参照することができます。

6. エクスポート値 vs. エクスポート参照

CommonJS:

  • エクスポート値はコピー:CommonJS では、モジュールのエクスポート値はエクスポートオブジェクトのコピーです。エクスポートオブジェクトを変更しても、他のモジュールで見える値には影響しません。

    math.js
    1
    let count = 0;
    2
    3
    module.exports = {
    4
    add: function (a, b) {
    5
    return a + b;
    6
    },
    7
    getCount: function () {
    8
    return count;
    9
    },
    10
    };
    11
    12
    // app.js
    13
    const math = require("./math");
    14
    console.log(math.getCount()); // 出力: 0
    15
    math.count = 10; // math.getCount() の戻り値は変わらない
    16
    console.log(math.getCount()); // 依然として: 0

ES Module:

  • エクスポート値は参照:ES Module では、モジュールのエクスポート値は元のオブジェクトの参照です。エクスポートオブジェクトを変更すると、他のモジュールにも反映されます。

    math.js
    1
    export let count = 0;
    2
    3
    export function add(a, b) {
    4
    return a + b;
    5
    }
    6
    7
    // app.js
    8
    import { count, add } from "./math.js";
    9
    console.log(count); // 出力: 0
    10
    count = 10; // math.js の count が変わる
    11
    import { count as newCount } from "./math.js";
    12
    console.log(newCount); // 依然として: 10

結論

  • CommonJS はサーバーサイド(Node.js)に適しており、モジュールは同期的にロードされ、簡単に使用できますが、ブラウザ環境では追加のツールが必要です。
  • ES Module は ECMAScript の標準モジュールシステムであり、静的解析と最適化をサポートし、現代のブラウザと Node.js に適しており、パフォーマンスと柔軟性が向上しています。

適切なモジュールシステムを選択することで、コードの保守性とパフォーマンスを向上させることができ、プロジェクトの要件と環境に応じた選択が重要です。