Edited at

Chrome、Safari、Firefoxで使えるJavaScriptのdynamic import(動的読み込み)

2019/06/06

dynamic importがステージ4になったので追記

2019/05/22

Firefox 67でdynamic importが使えるようになったので追記


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で確認できます。


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

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


参考記事