2019年10月13日日曜日

Node.js v12のES Modules

Node.js v12ではES Modulesの挙動が変わるよ、という話。

何はともあれ

まずはこれこれを見てください。公式の一次ソースです。

3行で。

  • .jsはES Modulesとして扱われるよ
  • import時に拡張子の指定が必須になるよ
  • でもこれまでとの互換性も確保するよ

.jsはES Modulesとして扱われるよ

今までは拡張子.jsはCommonJSで、ES Modulesでは.mjsという別の拡張子が必要でした。

しかし、今後はES Modules形式が一般的になることが容易に予想されます。
そうなったときでもES Modulesを使う場合は拡張子を.mjsにしなくてはいけないのか?という話。

もちろん好ましくはないので、Node.js v12からは.jsがCommonJSではなくES Modules形式として扱われるようになります。
そして新たな拡張子.cjsが導入されました。この拡張子がついているファイルはCommonJSとして扱われます。

ただし、いきなり変えてしまうと世界中が大混乱に陥るので、この挙動はパッケージ単位で設定可能で、デフォルトはCommonJSとして扱われます。

具体的には、package.jsonに以下のような記述があるとES Modules形式として扱われます。
{
  "type": "module"
}
この指定がない、あるいは"type": "commonjs"が指定されている場合は従来どおり.jsはCommonJSとして扱われます。

この挙動はパッケージごとに指定可能です。
具体的には、ロードされたファイルと同じディレクトリにあるpackage.jsonを探し、なければ親ディレクトリを順にたどって最初に見つかったpackage.jsonの設定が適用されます。

拡張子が必須になるよ

今まではES Modules形式でモジュールをロードするとき、拡張子が省略可能でした。

例えばmodule.mjsというモジュールがあった場合、以下のどちらの形式でも指定可能でした。
import "module.mjs";
import "module";
さらに、module/index.mjsというモジュールは以下のどれでも指定可能でした。
import "module/index.mjs";
import "module/index";
import "module";
でも、ブラウザのscriptタグでロードするときは拡張子が必須です。拡張子を勝手に補完してくれたり、index.mjsを補完してくれたりということはありません。
ネットワーク経由のパフォーマンスを考えれば当然ではあります。

Node.js v12からは、挙動をブラウザに合わせることにしました。
つまり拡張子やindex.mjsを省略することはできません

ちなみにこの挙動はES Modulesにのみ適用されるので、CommonJSではこれまで通り省略できます。

推測ですが、ES Modules形式はまだ実験段階の機能で使っている人はまだ少なく、最新情報をキャッチアップしている人が使っているから挙動を変えても大きな混乱はないと判断したんじゃないかと思います。
CommonJSは現状ほとんどの人が使っている&拡張子を省略している人が多いので、挙動を変えたら大混乱に陥るだろうという判断で現状のまま残したのではないかと。

とはいえ、互換性を考えてES Modules形式でも今までどおり拡張子を省略できるオプションもあります。コマンドラインで--es-module-specifier-resolution=nodeというオプションをつけてください。
注意点として、これは実行するファイル全てに適用されるので、上で書いた.jsの挙動のようにパッケージごとに挙動を変えることはできません

さらに詳しく

以上のような内容を10/25開催予定の関西Node学園 8時限目で話します。
当日はもう少し細かい実践的な内容も話す予定です。

0 件のコメント:

コメントを投稿