動的import()

公開日 · タグ付き ECMAScript ES2020

動的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.jsd8などの他のプラットフォームで特に役立ちます。これらは、何かがモジュールか通常のスクリプトかを判断するためのものです。ここでは、プラットフォーム間の一貫性を保ち、モジュールと通常のスクリプトの違いを明確にするために、同じ拡張子を使用しています。

モジュールをインポートするためのこの構文形式は、静的宣言です。モジュール指定子として文字列リテラルのみを受け入れ、ランタイム前の「リンク」プロセスによってローカルスコープにバインディングを導入します。静的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()に似ています)。つまり、importFunction.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()のサポート #