nomodule によるフォールバック

nomodule 属性を使うと、ES Modules をサポートしないブラウザ向けのフォールバックスクリプトを提供できます。モダンブラウザでは無視され、古いブラウザでのみ実行されます。

基本的な使い方

<!-- モダンブラウザ用(ES Modules 対応) -->
<script type="module" src="app.modern.js"></script>

<!-- 古いブラウザ用(ES Modules 非対応) -->
<script nomodule src="app.legacy.js"></script>

動作の仕組み

ブラウザtype="module"nomodule
ES Modules 対応実行する無視する
ES Modules 非対応無視する実行する
モダンブラウザ(Chrome, Firefox, Safari, Edge)

type="module" のスクリプトを実行し、nomodule は無視する

古いブラウザ(IE11 など)

type="module" は理解できず無視し、nomodule のスクリプトを実行する

なぜフォールバックが必要か

ES Modules を使ったコードには、古いブラウザで動作しない構文が含まれることがあります。

import / export 構文
アロー関数
テンプレートリテラル
クラス構文
const / let

これらをサポートしない IE11 などのために、トランスパイルしたコードを用意する必要があります。

ビルド設定の例

Webpack や Vite などのバンドラーで、モダン版とレガシー版の両方をビルドします。

// webpack.config.js の例
module.exports = [
  {
    // モダンブラウザ用
    entry: './src/index.js',
    output: {
      filename: 'app.modern.js'
    },
    target: 'es2020'
  },
  {
    // レガシーブラウザ用
    entry: './src/index.js',
    output: {
      filename: 'app.legacy.js'
    },
    target: 'es5',
    module: {
      rules: [{
        test: /\.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      }]
    }
  }
];

HTML での実装例

<!DOCTYPE html>
<html>
<head>
  <title>Module/NoModule Pattern</title>
</head>
<body>
  <div id="app"></div>
  
  <!-- モダンブラウザ用:軽量で高速 -->
  <script type="module" src="dist/app.modern.js"></script>
  
  <!-- レガシーブラウザ用:ポリフィル込みで重い -->
  <script nomodule src="dist/app.legacy.js"></script>
</body>
</html>

利点

バンドルサイズの最適化

モダンブラウザには最新の構文をそのまま配信できるため、不要なトランスパイルやポリフィルを含めずに済みます。

段階的な移行

古いブラウザのサポートを維持しながら、モダンな開発を続けられます。

Safari 10 の問題

Safari 10 には、nomodule を正しく無視しないバグがあります。

<!-- Safari 10 向けの修正 -->
<script>
  // Safari 10 は nomodule を理解するが、スクリプトを実行してしまう
  // この修正コードで対処
  (function() {
    var check = document.createElement('script');
    if (!('noModule' in check) && 'onbeforeload' in check) {
      var support = false;
      document.addEventListener('beforeload', function(e) {
        if (e.target === check) {
          support = true;
        } else if (!e.target.hasAttribute('nomodule') || !support) {
          return;
        }
        e.preventDefault();
      }, true);
      check.type = 'module';
      check.src = '.';
      document.head.appendChild(check);
      check.remove();
    }
  }());
</script>

現在の状況

2024年現在、IE11 のサポートを終了するプロジェクトが増えています。ターゲットブラウザによっては、nomodule フォールバックは不要かもしれません。

IE11 サポートが不要なら、nomodule は省略可能
モダンブラウザのみをターゲットにする場合は type="module" のみで OK

プロジェクトの要件に応じて、フォールバックの必要性を検討してください。