CommonJS と ES Module は、どちらも ファイルを分割して、他のファイルから使えるようにする仕組みです。
Node.js 公式ドキュメントでも、Node.js には CommonJS modules と ECMAScript modules の2つのモジュールシステムがあると説明されています。(Node.js)
ざっくり違い
| 比較 | CommonJS | ES Module |
|---|---|---|
| 主な構文 |
require, module.exports
|
import, export
|
| 略称 | CJS | ESM |
| 生まれ | Node.js 独自系 | JavaScript 標準 |
| 読み込み | 基本的に同期的 | 静的解析しやすい |
| ブラウザ対応 | そのままでは不可 | 標準対応 |
| 現代の推奨 | 既存Node.jsで多い | 新規開発ではおすすめ |
| TypeScriptとの相性 | 使える | 現代構成では使いやすい |
TypeScript 公式ドキュメントでも、現在重要なモジュールシステムとして ECMAScript modules / ESM と CommonJS / CJS が説明されています。(TypeScript)
CommonJS の書き方
CommonJS は、昔から Node.js でよく使われてきた形式です。
export 側
// math.js
function add(a, b) {
return a + b;
}
module.exports = {
add,
};
import 側
// app.js
const { add } = require("./math");
console.log(add(1, 2));
CommonJS では、読み込むときに require() を使います。
const { add } = require("./math");
外に公開するときは module.exports を使います。
module.exports = {
add,
};
ES Module の書き方
ES Module は、JavaScript 標準のモジュール形式です。
export 側
// math.js
export function add(a, b) {
return a + b;
}
import 側
// app.js
import { add } from "./math.js";
console.log(add(1, 2));
ES Module では、読み込むときに import を使います。
import { add } from "./math.js";
外に公開するときは export を使います。
export function add(a, b) {
return a + b;
}
一番大きな違い
一番大きな違いは、CommonJS は Node.js 独自寄り、ES Module は JavaScript 標準という点です。
CommonJS
Node.js で昔から使われてきた方式
ES Module
JavaScript 標準の方式
ブラウザでも Node.js でも使える
そのため、現代のフロントエンドや新規 TypeScript プロジェクトでは、基本的に ES Module がよく使われます。
default export の違い
ES Module では、default export がよく使われます。
// logger.js
export default function logger(message) {
console.log(message);
}
// app.js
import logger from "./logger.js";
logger("hello");
CommonJS では近い形として、こう書けます。
// logger.js
module.exports = function logger(message) {
console.log(message);
};
// app.js
const logger = require("./logger");
logger("hello");
ただし、CommonJS と ES Module を混ぜると、この default の扱いで混乱しやすいです。
named export の違い
ES Module の named export はこうです。
// user.js
export const name = "Taro";
export function getName() {
return name;
}
// app.js
import { name, getName } from "./user.js";
CommonJS ではこうです。
// user.js
const name = "Taro";
function getName() {
return name;
}
module.exports = {
name,
getName,
};
// app.js
const { name, getName } = require("./user");
見た目は似ていますが、内部の仕組みは別物です。
Node.js で ES Module を使う条件
Node.js で .js を ES Module として扱うには、よくある方法は package.json にこれを書くことです。
{
"type": "module"
}
この場合、.js ファイルで import / export が使えます。
import { add } from "./math.js";
逆に "type": "module" がない通常の Node.js プロジェクトでは、.js は CommonJS として扱われることが多いです。
const { add } = require("./math");
Node.js 公式ドキュメントでも、ES Module は .mjs、または package.json の "type": "module" などによって有効化されると説明されています。(Node.js)
拡張子の注意
ES Module では、Node.js で相対 import するときに拡張子を書くのが基本です。
import { add } from "./math.js";
CommonJS では省略されることが多いです。
const { add } = require("./math");
TypeScript で Node.js 向け ESM を書く場合も、ソースは .ts でも import は .js にすることがあります。
// src/index.ts
import { add } from "./math.js";
なぜなら、実行時には dist/math.js を読むからです。
TypeScript ではどうなる?
TypeScript では、書いたコードをどの形式に変換するかを tsconfig.json で決めます。
CommonJS に出力する例です。
{
"compilerOptions": {
"module": "CommonJS"
}
}
ES Module に近い Node.js 向け構成です。
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext"
}
}
新規の Node.js + TypeScript なら、ES Module 構成では NodeNext を使うことが多いです。
実務での使い分け
新規開発なら ES Module 寄り
import express from "express";
export function createApp() {
const app = express();
return app;
}
React、Next.js、Vite、Vue、Nuxt、モダンな Node.js + TypeScript では、ES Module が自然です。
既存の Node.js プロジェクトなら CommonJS も多い
const express = require("express");
module.exports = function createApp() {
const app = express();
return app;
};
古い Express アプリ、古い npm パッケージ、既存の Node.js バッチなどでは CommonJS がまだ多いです。
よくあるエラー
Cannot use import statement outside a module
これは、Node.js がそのファイルを CommonJS として扱っているのに、import を書いたときによく出ます。
import express from "express";
対策例です。
{
"type": "module"
}
または .mjs を使います。
app.mjs
ERR_REQUIRE_ESM
これは、CommonJS の require() で ES Module 専用パッケージを読み込もうとしたときによく出ます。
const somePackage = require("some-esm-only-package");
対策は、呼び出し側も ES Module に寄せることです。
import somePackage from "some-esm-only-package";
まとめ
実務的には、こう覚えるとよいです。
CommonJS
require / module.exports
Node.js で昔から使われてきた
既存プロジェクトで多い
ES Module
import / export
JavaScript 標準
ブラウザ・Node.js・TypeScriptで使いやすい
新規開発ではこちらがおすすめ
新しく Node.js + TypeScript backend を作るなら、基本は ES Module でよいです。
{
"type": "module"
}
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext"
}
}
既存の CommonJS プロジェクトでは、無理に全部変えるより、依存パッケージやビルド環境を確認しながら段階的に ES Module に寄せるのが安全です。