JavaScript アドバンス

JavaScript 動的モジュール

1. JavaScript Dynamic Import

Dynamic Import(動的インポート)は、以下の構文を使用します:

import(module);

Dynamic Import は ECMAScript 2020 (ES11) で導入されました。
これは、プログラムの開始時ではなく、ランタイム(実行時)にJavaScriptモジュールをロードするための手法です。

2. モダンなソフトウェア開発

モダンなソフトウェア開発では、インポートするコードを別々の「チャンク(Chunks)」に分割します。
モジュールが必要な時だけダウンロードされるこの手法には、以下のような多くの呼び名があります:

  • Dynamic Import(動的インポート)
  • Code Splitting(コード分割 / コードスプリッティング)
  • Lazy Loading(遅延読み込み / レイジーローディング)
  • Conditional Loading(条件付きロード)

Dynamic Import は、モジュール化された効率的なコードを実現するための最も強力な機能の一つです。
ファイルの上部に記述しなければならない Static Import(静的インポート) とは異なり、Dynamic Import は関数の中、条件分岐、イベントハンドラーなど、コード内のどこでも使用できます。

3. 構文 (Syntax)

import("./module.js")
  • 引数は、パスを解決するための文字列または式である必要があります。
  • インポートはモジュールスクリプト (<script type="module">) 内で実行する必要があります。
タイプロードされるタイミング
Staticimport { add } from './math.js';ロード時(起動時)
Dynamicconst math = await import('./math.js');必要な時(実行時)

4. パフォーマンスの向上 (Improved Performance)

モジュールを使用することで、ツールによる「コード分割(Code Splitting)」の実装が可能になり、パフォーマンスが向上します。これにより、ユーザーのブラウザはアプリケーション全体のコードを一度にロードするのではなく、その瞬間に使用している特定の機能に必要なJavaScriptモジュールのみをロードすればよくなります。

モジュール自体は言語機能ですが、WebpackRollupVite といったバンドラーと組み合わせることで、Tree-shaking(未使用コードの削除)、コードスプリッティング、Minification(最小化 / モニフィケーション)などの最適化を通じて、劇的なパフォーマンス改善をもたらします。

5. Dynamic Import の仕組み

動的モジュールは、モダンな async/await 構文を利用します。

実装例:

async function run() {
  const module = await import("./math.js");
  let result = module.add(2, 3);
  console.log(result);
}

run();

math.js:

// "add" 関数をエクスポート
export function add(a, b) {
  return a + b;
}

5.1 コード例の解説

このスクリプトの動作フローは以下の通りです:

  1. スクリプトの実行が開始される。
  2. run() 関数が定義され、呼び出される。
  3. run() 内で、math.js を動的にロードする。
  4. ロードが完了すると、エクスポートされた add() 関数へのアクセス権を取得する。
  5. add(2, 3) を呼び出し、結果 5 を得る。
  6. 結果を表示する。
ステップコード解説
1async function run()await を使用可能な関数を定義
2await import("./math.js")必要な時だけモジュールファイルをロード
3module.add(2, 3)モジュールからエクスポートされた関数を使用
4run()プロセスを開始

5.2 各ステップの詳細

ステップ 1. await を使用可能な関数を定義する

async function run() {
  • run() という名前の非同期関数(Asynchronous function)を定義します。
  • async キーワードは、その関数内で await が使用可能であることを示します。
  • await キーワードは、Promise(プロミス)が解決されるまで関数の実行を一時停止します。
  • このケースでは、モジュールのインポートが完了するのを待ちます。

ステップ 2. 必要な時だけモジュールファイルをロードする

const module = await import("./math.js");
  • import("./math.js") が Dynamic Import を定義します。
  • math.js モジュールはオンデマンド(必要に応じて)ロードされます。
  • math.js は、エクスポートされたコンテンツを含む Promise を返します。
  • await キーワードは、モジュールが完全にロードされるまで待機します。
  • ロードが完了すると、変数 modulemath.js からエクスポートされたデータが格納されます。

ステップ 3. モジュールのエクスポート関数を使用する

let result = module.add(2, 3);
  • インポートされたモジュールから、エクスポートされている add() 関数を呼び出します。
  • 引数として 23 を渡します。
  • 関数はそれらを加算し、5 を返します。
  • 結果は result 変数に保存されます。

ステップ 4. プロセスを開始する(関数の呼び出し)

run();

実行時の挙動:

  1. math.js モジュールを動的にロードする。
  2. モジュールのロード完了を待機する。
  3. モジュールの add() 関数を実行する。
  4. 結果を表示する。

6. もう一つの実例:温度変換

実装例:

async function run(x) {
  const module = await import("./temperatures.js");
  let celsius = module.toCelsius(x);
  document.getElementById("demo").textContent = celsius + " Celsius";
}

run(50);

temperatures.js:

// 華氏(Fahrenheit)を摂氏(Celsius)に変換
export function toCelsius(farenheit) {
  return (farenheit - 32) * 5 / 9;
}

// 摂氏(Celsius)を華氏(Fahrenheit)に変換
export function toFahrenheit(celsius) {
  return (celsius * 9 / 5) + 32;
}

7. Dynamic Import の主要機能

  • Lazy Loading(遅延読み込み): コードが必要になった瞬間にだけロードします。
  • パフォーマンスの最適化: 初期ロード時のサイズを削減し、ページ表示速度を高速化します。
  • 条件付きインポート (Conditional Imports): 条件に基づいて異なるモジュールをインポートできます。

7.1 条件付きロード (Conditional Loading)

ユーザーの権限に応じて特定の管理ツールをロードする例:

if (user.isAdmin) {
  const adminTools = await import("./admin-tools.js");
  adminTools.init();
}