255
177

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

全モダンブラウザで使えるJavaScriptのdynamic import(動的読み込み)

Last updated at Posted at 2017-11-22

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

さらに、モジュールをimport()を使って「関数のように」呼び出すためのdynamic importという仕組みが2020年に公開されるECMAScriptの仕様で導入されます(tc39/proposal-dynamic-import)。Google Chrome、Firefox、Safariではではモジュールのdynamic importに対応しており、ブラウザでその挙動を確認できます。

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

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

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

各サンプルは、Google Chrome、Safari、Firefox、Edgeで動作します。

これまでの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やFirefox、Edgeでも動作

Safariは11.1から、Firefoxでは67から、Edgeでは79からdynamic importに対応済みです。ちなみにiOS Safariでも動作します。

Can I useより

Firefox、Safariで動作している様子は次のとおりです。

▼ Firefoxで動作している様子

▼ 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するならば次のようにします。

main.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を用いて実現していた仕組みが、ブラウザの標準機能として実装されつつあり、さらに動的読み込みもというのは明るい話題ではないでしょうか。

参考記事

255
177
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
255
177

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?