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の使い方について解説します。
export class Sub {
subMethod() {
alert("this is Sub Class Method");
}
}
各サンプルは、Google Chrome、Safari、Firefox、Edgeで動作します。
これまでのimport
import
はJavaScriptのトップレベルでしか用いることができず、モジュールは即座に読み込まれる仕様でした。
// 即座にsub.jsが読み込まれる
import {Sub} from './sub.js'
const sub = new Sub();
sub.subMethod(); // 「this is Sub Class Method」というアラートが表示される。
dynamic import
Dynamic importでは、 import(モジュール名)
を用いてモジュールを動的に読み込みます。返り値はPromise
(Promise - JavaScript | MDN)です。
// 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
なので、 await と asyncを用いて、次のようにモジュールの読み込みを待つことも可能です。
async function main() {
const module = await import('./sub.js');
const sub = new module.Sub();
sub.subMethod();
}
main();
※ dynamic importに対応したブラウザでご確認ください。
一定時間後にimport
モジュールが動的に読み込まれていることをわかりやすくするために、次のように2秒後にモジュールの読み込み・実行を行う処理を書きました。
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)で動作しました。
import('./sub.js')
.then(({ Sub }) => {
// 動的に読み込まれたSubクラス
const sub = new Sub();
sub.subMethod();
});
await / async
を使うとこのようになります。
async function main() {
const { Sub } = await import('./sub.js');
const sub = new Sub();
sub.subMethod();
}
main();
※ dynamic importに対応したブラウザでご確認ください。
複数クラスのimport
オブジェクト形式で展開する場合、複数のクラスをimport
するならば次のようにします。
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を用いて実現していた仕組みが、ブラウザの標準機能として実装されつつあり、さらに動的読み込みもというのは明るい話題ではないでしょうか。