mjsファイルとは? 🧐
.mjs
ファイルが登場した背景 📜
歴史的に、Node.jsなどのJavaScript実行環境では CommonJS という異なるモジュールシステムが広く使われてきました。CommonJSでは require()
と module.exports
を使用します。
しかし、ウェブブラウザや新しいJavaScriptの仕様では、ES Modules が標準となりました。ここで問題が発生しました。
-
.js
ファイルの曖昧さ: ある.js
ファイルがCommonJS形式なのか、ES Modules形式なのかを、ファイルの内容を解析せずに判断するのは難しいです。 - Node.jsの互換性問題: Node.jsはCommonJSが主流だったため、ES Modulesの導入には互換性の課題がありました。
この問題を解決するために、Node.jsコミュニティを中心に .mjs
拡張子("Module JavaScript" の略と言われることが多い)が導入されました。これにより、ファイル拡張子を見ただけで、それがES Modules形式のファイルであることが明確にわかるようになりました。
.mjs
ファイルと .js
ファイルの主な違い ✨
特徴 |
.mjs ファイル |
.js ファイル(デフォルト) |
---|---|---|
モジュール形式 |
ECMAScript Modules (ES Modules) として扱われることが保証されます。import / export 構文が機能します。 |
Node.js環境では、デフォルトで CommonJS として扱われます(ただし、package.json に "type": "module" が設定されているディレクトリ内ではES Modulesとして扱われることもあります)。<br>ブラウザ環境では、HTMLの <script type="module"> で読み込まれた場合はES Modules、それ以外は従来のスクリプトとして扱われます。 |
厳格モード | デフォルトで厳格モード (strict mode) で実行されます。 | デフォルトでは厳格モードではありません(ただし、CommonJSモジュールは暗黙的に厳格モードになることがあります)。 |
this の挙動 |
最上位スコープの this は undefined です。 |
最上位スコープの this は module.exports を指します。 |
解決ルール | 異なる解決ルールが適用される場合があります(例: .js , .json , .node の順で解決するなど)。 |
異なる解決ルールが適用される場合があります(例: .js , .json , .node の順で解決するなど)。 |
.mjs
ファイルの活用シナリオ 🚀
-
Node.js 環境でのES Modulesの使用:
Node.jsの古いバージョンや、package.json
の"type": "commonjs"
が設定されているプロジェクトで、ES Modulesを明示的に使用したい場合に.mjs
拡張子を利用します。// my-module.mjs (ES Modules) export function greet(name) { return `Hello, ${name}!`; } // app.js (CommonJS環境から.mjsを読み込む場合) // Node.js v13.2.0 以降では、動的 import() で .mjs を読み込めます async function run() { const { greet } = await import('./my-module.mjs'); console.log(greet('World')); // Hello, World! } run();
-
ブラウザ環境での明確化:
ブラウザで直接ES Modulesを読み込む場合、HTMLの<script type="module" src="app.mjs"></script>
とすることで、そのファイルがES Modulesであることをより明確に示せます(ただし、.js
拡張子でもtype="module"
があればES Modulesとして扱われます)。 -
モジュールバンドラーとの連携:
WebpackやRollupなどのモジュールバンドラーは、.mjs
拡張子をES Modulesとして正しく認識し、処理してくれます。これにより、様々なモジュール形式が混在するプロジェクトでも、バンドラーが適切に処理できるようになります。
.mjs
ファイルのサンプルコードと使用した場合・使用しなかった場合の比較 📊
ここでは、Node.js環境におけるES Modulesの扱いを比較してみましょう。
.mjs
ファイルを使用した場合)
📌 サンプルコード (message.mjs
:
// これはES Modules形式
export const hello = "Hello from .mjs!";
export function sayGoodbye() {
return "Goodbye from .mjs!";
}
app.js
:
// CommonJS環境のファイル
// .mjs ファイルは、Node.jsの動的 import() を使って読み込む
async function run() {
try {
const { hello, sayGoodbye } = await import('./message.mjs');
console.log(hello); // 出力: Hello from .mjs!
console.log(sayGoodbye()); // 出力: Goodbye from .mjs!
} catch (error) {
console.error("Error importing .mjs:", error);
}
}
run();
// CommonJS形式で何かをエクスポートする例 (app.js自体はCommonJS)
module.exports = {
appName: "My Hybrid App"
};
実行コマンド:
node app.js
この場合、app.js
はCommonJSモジュールとして実行されつつ、import()
を使って .mjs
ファイルをES Modulesとして非同期に読み込み、そのエクスポートされた値を利用できます。
.mjs
ファイルを使用しなかった場合の比較(.js
ファイルと type: "module"
)
📌 Node.jsでES Modulesを.js
ファイルで扱いたい場合、最も一般的な方法は、プロジェクトの package.json
に "type": "module"
を追加することです。これにより、そのディレクトリ内(およびサブディレクトリ)のすべての .js
ファイルがデフォルトでES Modulesとして扱われるようになります。
package.json
:
{
"name": "my-es-module-app",
"version": "1.0.0",
"type": "module", // ✨ これが重要 ✨
"main": "app.js",
"scripts": {
"start": "node app.js"
}
}
message.js
:
// package.jsonで "type": "module" が設定されているので、これはES Modules形式
export const hello = "Hello from .js (ESM)!";
export function sayGoodbye() {
return "Goodbye from .js (ESM)!";
}
app.js
:
// package.jsonで "type": "module" が設定されているので、これもES Modules形式
import { hello, sayGoodbye } from './message.js'; // ✨ .js 拡張子が必要になることが多い ✨
console.log(hello); // 出力: Hello from .js (ESM)!
console.log(sayGoodbye()); // 出力: Goodbye from .js (ESM)!
// ES Modules形式で何かをエクスポートする例
export const appVersion = "1.0";
実行コマンド:
node app.js
この設定では、すべての .js
ファイルがES Modulesとして扱われるため、CommonJSの require()
や module.exports
を使用するとエラーになります。プロジェクト全体をES Modulesに移行する場合に適しています。
Discussion