ESModuleとCommonJS

2024/03/26に公開

ESModuleとCommonJSを理解するのに実用例を出すとわかりやすいので、以下にそれぞれの最小構成を出します。

ES Modulesの使用例

ES Modulesは、モダンなJavaScriptアプリケーションにおけるコードのモジュール化、再利用を容易にする仕組みです。ここでは、ブラウザでHTMLファイルからJavaScriptのES Moduleを読み込む最小構成を示します。2017年以降のバージョンのブラウザは対応しています。

index.html

HTMLファイルからJavaScriptのES Moduleを読み込み、ボタンクリックでメッセージを表示します。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>ESModule</title>
  </head>
  <body>
    <h1>ESModule</h1>
    <button id="messageButton">Show Message</button>
    <p id="messageDisplay"></p>
    <script type="module">
      import { showMessage } from "./main.js";
      showMessage();
    </script>
  </body>
</html>

main.js

showMessage関数をエクスポートし、ボタンクリックイベントを設定します。

export function showMessage() {
  document.getElementById("messageButton").addEventListener("click", () => {
    document.getElementById("messageDisplay").textContent = "Hello World!";
  });
}

CommonJSの使用例

サーバサイドのNodejsのデフォルトで使用されるモジュールシステム。

index.js

greet.jsモジュールを読み込み、その関数を実行します。

const { greet } = require("./main");
greet();

greet.js

greet関数を定義し、module.exportsを通じてエクスポートします。

function greet() {
  console.log("Hello, World!");
}

module.exports = { greet };

よくハマるポイントと解決策

CommonJSからES Modulesをrequire()構文で読み込むことができない。

例えば、CommonJSのindex.jsからnanoidを使おうとすると以下のようにエラーが発生します。

const nanoid = require("nanoid");
console.log("nanoid", nanoid());

// error message
onst nanoid = require("nanoid");
               ^
ReferenceError: require is not defined in ES module scope, you can use import instead

解決策

1. Dynamic Importsを使う。

// index.js
(async () => {
  const { nanoid } = await import("nanoid");
  console.log(nanoid());
})();

Dynamic ImportsはPromiseオブジェクトを返すので、トップレベルスコープ以外で使用する必要があります。2回目以降に読み込む時は、メモリにキャッシュされた以前に読み込んだときのエクスポートを返します。

2. アプリケーションをESMとして扱う。

// package.jsonに追加
{
  "type": "module",
}

// index.js
import { nanoid } from "nanoid";
console.log(nanoid());

package.jsonのtypeをmoduleに変更します。

つまりどちらを使えば良い?

基本的には、ES Modulesを使用することを推奨します。CommonJSを採用する場合はDynamic Importsを活用することでES Modulesのライブラリを読み込むことができます。

Discussion