IE11 が対象外になったことで JavaScript は webpack などのモジュールバンドラ無しで書いてもブラウザで動作するようになりました。ウェブアプリなど React を利用するシーンでは、まだまだモジュールバンドラを利用したほうがいいと思いますが(TypeScriptで書きたいですよね)、ウェブサイトではモジュールバンドラ無しで十分だと思います。

Rails7 でも Webpacker が剥がされて、Node.js レスな環境を提供しています。

JavaScript モジュール

具体的には JavaScript モジュールを利用します。ブラウザのサポート状況を確認しても、IE以外は問題なさそうです。以下簡単なサンプルです。

class EventEmitter {
  ...
}

export default EventEmitter;

main.js で、EventEmitter クラスを import して使います。拡張子は必須です。拡張子が不要なのは webpack の設定でしたよね、そういえば。

import EventEmitter from './libs/EventEmitter.js';

const ev = new EventEmitter();

console.log(ev);

HTML側に設定を書きます。

<script type="module" src="/assets/js/libs/utils/EventEmitter.js"></script>
<script type="module" src="/assets/js/main.js"></script>

type 属性に module をつければ OK です。main.js にも付けましょう。これだけで OK 。Node.js レスで JavaScript を記述することができます。

ライブラリの import

ではライブラリの扱いはどうしましょう?グローバルで読み込むのもアリですが、import で読み込むことも可能です。import maps という web標準の機能を利用します。が、しかし、この機能はまだ使えるブラウザ環境が少ないです。

Import maps | Can I use… Support tables for HTML5, CSS3, etc

大きなところでは Safari がダメですね。意外に Edge はイケる。これは Polyfill を利用します。

guybedford/es-module-shims: Shims for new ES modules features on top of the basic modules support in browsers

<script async src="https://ga.jspm.io/npm:es-module-shims@1.5.8/dist/es-module-shims.js"></script>

<!-- https://generator.jspm.io/#U2NhYGBkDM0rySzJSU1hKEpNTC5xMLTQM9Az0C1K1jMAAKFS5w0gAA -->
<script type="importmap">
{
  "imports": {
    "react": "https://ga.jspm.io/npm:react@18.0.0-rc.0/index.js"
  },
  "scopes": {
    "https://ga.jspm.io/npm:react@18.0.0-rc.0/": {
      "object-assign": "https://ga.jspm.io/npm:object-assign@4.1.1/index.js"
    }
  }
}
</script>

<script type="module">
  import react from 'react';
  console.log(react);
</script>

ブラウザサポート状況はこんなかんじ。

普通に使う程度なら問題無さそう。でもグローバルとして扱えばよいかなと個人的には思います。

ファイルを大量にロードするが問題ないのか?

<script type="module" src="/assets/js/libs/utils/EventEmitter.js"></script>
<script type="module" src="..."></script>
<script type="module" src="..."></script>
...

こういう記述が続くので、ロードするファイル数がかなり多くなります。gulp などで、JavaScript ファイルを一個にまとめて通信量を減らした経験がある人には眉をひそめる記述でしょう。

が、それは HTTP/1.1 時代のお話。現代は HTTP/2 の時代です。HTTP/2 はストリームという概念を導入したことで、並列で通信が可能になりました。HTTP/1.1 のように、Aのロード終了後にBをロード…というシリアルな処理にはならない模様です。画像もスプライト画像にして通信量を減らす、という時代もありましたがそれも過去の話になりました。

デメリット

HTML に module を記述するのが結構面倒です。大量にモジュールを扱うウェブアプリなどには不向きでしょう。ウェブサイト向きかなと思いました。