動的import()
は、静的なimport
と比較して新しい機能を可能にする、新しい関数のような形式のimport
を導入します。この記事では、この2つの方法を比較し、新機能の概要を示します。
静的import
(復習) #
Chrome 61は、モジュール内のES2015 import
文のサポートを提供してリリースされました。
./utils.mjs
にある次のモジュールを考えてみましょう。
// Default export
export default () => {
console.log('Hi from the default export!');
};
// Named export `doStuff`
export const doStuff = () => {
console.log('Doing stuff…');
};
./utils.mjs
モジュールを静的にインポートして使用する方法を以下に示します。
<script type="module">
import * as module from './utils.mjs';
module.default();
// → logs 'Hi from the default export!'
module.doStuff();
// → logs 'Doing stuff…'
</script>
注記: 上記の例では、.mjs
拡張子を使用して、それが通常のスクリプトではなくモジュールであることを示しています。ウェブ上では、ファイルが正しいMIMEタイプ(例:JavaScriptファイルの場合はtext/javascript
)でContent-Type
HTTPヘッダーで提供されている限り、ファイル拡張子は実際には重要ではありません。
.mjs
拡張子は、MIMEタイプやtype="module"
などの必須フックがないNode.jsやd8
などの他のプラットフォームで特に役立ちます。これらは、何かがモジュールか通常のスクリプトかを判断するためのものです。ここでは、プラットフォーム間の一貫性を保ち、モジュールと通常のスクリプトの違いを明確にするために、同じ拡張子を使用しています。
モジュールをインポートするためのこの構文形式は、静的宣言です。モジュール指定子として文字列リテラルのみを受け入れ、ランタイム前の「リンク」プロセスによってローカルスコープにバインディングを導入します。静的import
構文は、ファイルの一番上にのみ使用できます。
静的import
は、静的解析、バンドルツール、ツリーシェイキングなどの重要なユースケースを可能にします。
場合によっては、以下のようなことが役立ちます。
- オンデマンド(または条件付きで)モジュールをインポートする
- モジュール指定子をランタイム時に計算する
- モジュール(モジュールではなく)内からモジュールをインポートする
これらは静的import
ではどれも不可能です。
動的import()
🔥 #
動的import()
は、これらのユースケースに対応する、新しい関数のような形式のimport
を導入します。import(moduleSpecifier)
は、要求されたモジュールのモジュール名前空間オブジェクトに対するPromiseを返します。これは、モジュール自体だけでなく、すべてのモジュールの依存関係のフェッチ、インスタンス化、評価後に作成されます。
./utils.mjs
モジュールを動的にインポートして使用する方法を以下に示します。
<script type="module">
const moduleSpecifier = './utils.mjs';
import(moduleSpecifier)
.then((module) => {
module.default();
// → logs 'Hi from the default export!'
module.doStuff();
// → logs 'Doing stuff…'
});
</script>
import()
はPromiseを返すため、then
ベースのコールバックスタイルの代わりにasync
/await
を使用できます。
<script type="module">
(async () => {
const moduleSpecifier = './utils.mjs';
const module = await import(moduleSpecifier)
module.default();
// → logs 'Hi from the default export!'
module.doStuff();
// → logs 'Doing stuff…'
})();
</script>
注記: import()
は関数呼び出しのように見えますが、括弧を使用するだけの構文として指定されています(super()
に似ています)。つまり、import
はFunction.prototype
を継承しないため、call
またはapply
することはできません。また、const importAlias = import
のようなものは機能しません。実際には、import
はオブジェクトでさえありません!ただし、実際にはこれはそれほど問題になりません。
小さなシングルページアプリケーションでのナビゲーション時に、動的import()
がモジュールの遅延読み込みを可能にする例を以下に示します。
<!DOCTYPE html>
<meta charset="utf-8">
<title>My library</title>
<nav>
<a href="books.html" data-entry-module="books">Books</a>
<a href="movies.html" data-entry-module="movies">Movies</a>
<a href="video-games.html" data-entry-module="video-games">Video Games</a>
</nav>
<main>This is a placeholder for the content that will be loaded on-demand.</main>
<script>
const main = document.querySelector('main');
const links = document.querySelectorAll('nav > a');
for (const link of links) {
link.addEventListener('click', async (event) => {
event.preventDefault();
try {
const module = await import(`/${link.dataset.entryModule}.mjs`);
// The module exports a function named `loadPageInto`.
module.loadPageInto(main);
} catch (error) {
main.textContent = error.message;
}
});
}
</script>
動的import()
によって実現される遅延読み込み機能は、正しく適用すれば非常に強力です。実証のために、Addyは、コメントを含むすべての依存関係を最初にロード時に静的にインポートしたHacker News PWAの例を変更しました。更新されたバージョンでは、動的import()
を使用してコメントを遅延読み込みし、ユーザーが実際に必要とするまで、ロード、解析、コンパイルのコストを回避しています。
注記: アプリケーションが別のドメインからスクリプトをインポートする場合(静的または動的のいずれか)、スクリプトは有効なCORSヘッダー(例:Access-Control-Allow-Origin: *
)で返される必要があります。これは、通常のスクリプトとは異なり、モジュールスクリプト(とそのインポート)がCORSを使用してフェッチされるためです。
推奨事項 #
静的import
と動的import()
の両方とも有用です。それぞれに非常に異なるユースケースがあります。特に、画面の上部に表示されるコンテンツなど、最初の描画の依存関係には、静的import
を使用してください。その他のケースでは、動的import()
を使用してオンデマンドで依存関係を読み込むことを検討してください。
動的import()
のサポート #
- Chrome: バージョン63以降でサポート
- Firefox: バージョン67以降でサポート
- Safari: バージョン11.1以降でサポート
- Node.js: バージョン13.2以降でサポート
- Babel: サポート済み