# 動的 & 非同期コンポーネント

このページはすでに コンポーネントの基本 を読んでいる事を前提としています。コンポーネントをよく知らない方はそちらを先にお読みください。

# 動的コンポーネントにおける keep-alive の利用

まず、タブインタフェースにおいてコンポーネントを切り替える is 属性を使ったとします:

<component :is="currentTabComponent"></component>
1

しかし、コンポーネントを切り替える時、コンポーネントの状態を保持したり、パフォーマンスの理由から再レンダリングを避けたいときもあるでしょう。例えば、タブインターフェースを少し拡張した場合:

See the Pen Dynamic components: without keep-alive by Vue (@Vue) on CodePen.

Posts タブの投稿を選択し、 Archive タブに切り替えてから Posts に戻ると、選択していた投稿が表示されないことに気づくでしょう。これは、新しいタブに切り替えるたびに、Vue が currentTabComponent の新しいインスタンスを作成するからです。

動的コンポーネントの再生成は通常は便利な挙動です。しかし、このケースでは最初に生成されたタブコンポーネントのインスタンスがキャッシュされるのが好ましいでしょう。この問題を解決するためには、動的コンポーネントを <keep-alive> 要素で囲みます:

<!-- アクティブでないコンポーネントはキャッシュされます! -->
<keep-alive>
  <component :is="currentTabComponent"></component>
</keep-alive>
1
2
3
4

以下の結果を確認してみてください:

See the Pen Dynamic components: with keep-alive by Vue (@Vue) on CodePen.

このように Posts タブがレンダリングされていなくても、自身の状態(選択された投稿)を保持するようになります。

<keep-alive> の詳細な情報については API リファレンス を参照してください。

# 非同期コンポーネント

大規模なアプリケーションでは、アプリケーションを小さなまとまりに分割し、必要なときにだけコンポーネントをサーバーから読み込みたい場合があるでしょう。これを可能にするために、 Vue には defineAsyncComponent メソッドがあります:

const { createApp, defineAsyncComponent } = Vue

const app = createApp({})

const AsyncComp = defineAsyncComponent(
  () =>
    new Promise((resolve, reject) => {
      resolve({
        template: '<div>I am async!</div>'
      })
    })
)

app.component('async-example', AsyncComp)
1
2
3
4
5
6
7
8
9
10
11
12
13
14

見て分かるとおり、このメソッドは Promise を返すファクトリ関数を受けます。サーバーからコンポーネント定義を取得したら Promise の resolve コールバックが呼ばれるべきです。また、読み込みが失敗したことを示すために reject(reason) を呼ぶこともできます。

ファクトリ関数の中で Promise を返すことができるので、 Webpack 2 以降と ES2015 の構文では以下のように書くこともできます:

import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() =>
  import('./components/AsyncComponent.vue')
)

app.component('async-component', AsyncComp)
1
2
3
4
5
6
7

コンポーネントのローカル登録 でも、 defineAsyncComponent を利用できます。

import { createApp, defineAsyncComponent } from 'vue'

createApp({
  // ...
  components: {
    AsyncComponent: defineAsyncComponent(() =>
      import('./components/AsyncComponent.vue')
    )
  }
})
1
2
3
4
5
6
7
8
9
10

# Suspense との併用

非同期コンポーネントはデフォルトで suspensible です。これは非同期コンポーネントが <Suspense> を親に持ったとき、 <Suspense> の非同期の依存として取り扱われることを意味しています。このケースでは、読み込みの状態は <Suspense> から制御され、コンポーネント自身が持つ loading や error, delay, timeout といったオプションは無視されます。

非同期コンポーネントのオプションに suspensible: false を指定することで、 Suspense の制御から外すことができ、常にコンポーネントが自身の読み込み状態を制御することができます。

API リファレンス で利用可能なオプションのリストを確認できます。