背景
現在開発しているPJはフロントエンドをAngular、バックエンドをNode.jsで実装している。Node.jsはTypeScriptで実装している。まだ勉強中の身なので環境構築時には意識したことがなかったが、同じTypeScriptなのにモジュールをインポートしたりエクスポートする際の記述の方法が異なっていたので気になって調べたところ、ESModulesとCommonJSというものに出会ったので、理解を整理するためにまとめる。
ESMoudlesとCommonJSの違い
それぞれの違いについてまとめる。
構文の違い
以下のような書き方の違いがある。
ESModules
// モジュールのエクスポート
// moduleA.mjs
export const add = (a, b) => a + b;
// モジュールのインポート
// main.mjs
import { add } from './moduleA.mjs';
console.log(add(3, 5)); // 8
CommonJS
// モジュールのエクスポート
// moduleA.js
const add = (a, b) => a + b;
module.exports = add;
// モジュールのインポート
// main.js
const addFunction = require('./moduleA.js');
console.log(addFunction(3, 5)); // 8
実行環境
ESModules
ブラウザやNode.jsの新しいバージョンではサポートされている。以下のサイトでブラウザごとの対応状況を確認できる。
CommonJS
Node.jsではデフォルトで採用されているが、ブラウザではサポートされていない。ただ、Node.js上で動作するテンプレートエンジン(ejsやpugなど)を使用すればもちろんビューを作成し、ブラウザ上で動作させることが可能である。
モジュールの読み込み
ESModules
非同期でモジュールを読み込む。アプリケーションのパフォーマンス向上につながる。
CommonJS
同期的にモジュールを読み込む。そのため、複数のモジュールの読み込みが並行して行われることはなく、1つずつ順番に読み込まれる。
静的解析ができるか
ESModules
静的解析が可能。そのため、モジュールのパスに誤りなどがあれば、コンパイル時に指摘することが可能。
CommonJS
静的解析が不可能。
なぜ二つが存在するのか
そもそもなぜこのように2つのモジュールが存在するのか。これを理解するにはJavaScriptにおけるモジュールシステムの歴史を学ぶ必要がある。こちらで詳しく書かれているので、詳細は以下の記事に任せる。
ESModulesとCommonJSの統合
ESModulesとCommonJSが混同したPJの場合、フロントを実装する時とバックを実装するときで記述方法が異なったりするので、個人的にはどちらかに統一したいと思った。ここまで書いてきた通り、ESModulesの方が静的解析が可能だったり、Node.jsもESModulesをサポートし始めたことから潮流はESModulesに来ている。
Angularなどのフロントエンドフレームワーク、最新のNode.jsに関してはデフォルトでESModulesなので気にする必要はないが、古いバージョンのNode.jsを使用する場合には、以下のような手順でコンバージョンする必要がある。
以下Google翻訳
CommonJS プロジェクトを ESM に移動するにはどうすればよいですか?
x"type": "module"package.json に追加します。
package.json 内"main": "index.js"の に置き換えます。"exports": "./index.js"
"engines"package.json のフィールドを Node.js 16: に更新します"node": ">=16"。
'use strict';すべての JavaScript ファイルから削除します。
require()すべての/を/module.exportに置き換えます。importexport
インポートには完全な相対ファイル パスのみを使用します: import x from '.';→ import x from './index.js';。
TypeScript 型定義 (たとえば、index.d.ts) がある場合は、ESM インポート/エクスポートを使用するようにそれを更新します。
Node.js 組み込みインポートのnode:プロトコルを使用します。
これを見た感想としては、
- requireをimport fromに書き換えたり、module.exportsの書き換えが重い。
- CommonJSではモジュールのパスを拡張子まで含めて書いていないと思うので、相対パスで拡張子まで含めて書くのはとても重い。
- 依存パッケージがESModulesをサポートしている必要があるので、それの確認が重い。
実際にESModulesに移行している記事を調べてみるとこれ以外にもいろんな障壁があるらしい。
PJの規模にもよるだろうが、移行するのはめっちゃしんどそうというのが、個人的な感想である。1から作るときはNode.jsで最初からESModulesを使おうと思った。
参考