JavaScriptでは他のJSファイルを読み込みES Modulesという仕組みがあり、Chrome、Edge、Safariなどブラウザのサポートが拡大しつつあります(参考記事「JavaScriptでモジュールを使う時代に! ブラウザで覚えるES Modules入門」)。

さらに、モジュールをimport()を使って「関数のように」呼び出すためのdynamic importという仕組みが策定中(※ tc39/proposal-dynamic-import)です。Safari 11、Chrome 63ではモジュールのdynamic importに対応しており、ブラウザでその挙動を確認できます。

dynamic importのメリットは、任意のタイミングでモジュールを読み込めること。例えば、ページの初期表示に必要なJavaScriptだけを読み込んでおき、コンテンツが展開する度に必要なモジュールを遅延ロードすれば、ページの初期表示時の処理負荷が軽減できます。

本エントリーでは次のようなSubクラスを定義したsub.jsモジュールの読み込みを通して、dynamic importの使い方について解説します。

sub.js
export class Sub {
  subMethod() {
    alert("this is Sub Class Method");
  }
}

各サンプルは、Safari 11かChrome 63以降で確認できます。

これまでのimport

importはJavaScriptのトップレベルでしか用いることができず、モジュールは即座に読み込まれる仕様でした。

main1.js
 // 即座にsub.jsが読み込まれる
import {Sub} from './sub.js'

const sub = new Sub();
sub.subMethod(); // 「this is Sub Class Method」というアラートが表示される。

dynamic import

Dynamic importでは、 import(モジュール名)を用いてモジュールを動的に読み込みます。返り値はPromisePromise - JavaScript | MDN)です。

main2.js
// import()のタイミングでsub.jsが読み込まれる
import('./sub.js')
  .then(module => {
      // 動的に読み込まれたSubクラス
      const sub = new module.Sub();
      sub.subMethod();  
    });

resolve()時の引数にモジュールを受け取るため、new module.Sub()とすればsub.js内のSubクラスにアクセスできます。

※ dynamic importに対応したブラウザでご確認ください。

await/asyncを用いてimport

返り値が Promise なので、 awaitasyncを用いて、次のようにモジュールの読み込みを待つことも可能です。

main3.js
async function main() {
  const module = await import('./sub.js');
  const sub = new module.Sub();
  sub.subMethod();
}

main();

※ dynamic importに対応したブラウザでご確認ください。

image.png

一定時間後にimport

モジュールが動的に読み込まれていることをわかりやすくするために、次のように2秒後にモジュールの読み込み・実行を行う処理を書きました。

main4.js
setTimeout(() => {
  import('./sub.js')  // 2秒後にモジュールを読み込み
    .then(module => {
        // 読み込み完了後にSubを使用する
        const sub = new module.Sub();
        sub.subMethod();
      });
}, 2000);

※ dynamic importに対応したブラウザでご確認ください。

Chromeの開発者ツールで確認すると、ページ表示時にはモジュールは読み込まれず、2秒経って読み込まれることが確認できます。

Safariでは現行版で対応済

Safariは現行版の11でdynamic importに対応済。ちなみにiOS Safariでも動作します。

オブジェクト形式でモジュールを展開する

前述の例では .then(module =>のようにmodule変数にモジュールを展開していましたが、次のようにするとnew Sub()Subをインスタンス化できるようになります。

この方法は「tc39/proposal-dynamic-import」に記載はありませんでしたが、Safari 11、Chrome 64 (canary)で動作しました。

main5.js
import('./sub.js')
  .then(({ Sub }) => {
  // 動的に読み込まれたSubクラス
  const sub = new Sub();
  sub.subMethod();
});

await / asyncを使うとこのようになります。

main6.js
async function main() {
  const { Sub } = await import('./sub.js');

  const sub = new Sub();
  sub.subMethod();
}

main();

※ dynamic importに対応したブラウザでご確認ください。

複数クラスのimport

オブジェクト形式で展開する場合、複数のクラスをimportするならば次のようにします。

main7.js
async function main() {
  const { Sub, Sub2 } = await import('./sub.js');

  const sub = new Sub();
  sub.subMethod();

  const sub2 = new Sub2();
  sub2.subMethod2();
}

main();

※ dynamic importに対応したブラウザでご確認ください。

ますます強力になるJavaScriptのモジュール

JavaScriptの大規模開発においてはモジュールによる処理の分割は必須。従来はwebpack(参考記事「最新版で学ぶwebpack 3入門」)やBrowserifyを用いて実現していた仕組みが、ブラウザの標準機能として実装されつつあり、さらに動的読み込みもというのは明るい話題ではないでしょうか。

参考記事

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.