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

最終更新日: 2019年7月22日

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

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

まず、is 属性を利用してタブインタフェースのコンポーネントを切り替えることができます:

<component v-bind:is="currentTabComponent"></component>

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

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

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

<!-- インアクティブなコンポーネントはキャッシュされます! -->
<keep-alive>
  <component v-bind:is="currentTabComponent"></component>
</keep-alive>

以下のようになります:

このように Posts タブがレンダリングされていなくても、自身の状態(選択された投稿)を保持するようになります。完全なコードは この fiddle を参照してください。

<keep-alive> にラップされるコンポーネントは、全て name を持つ必要があります。 コンポーネントの name オプションを使うか、ローカル/グローバル登録を使用してください。

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

非同期コンポーネント

大規模なアプリケーションでは、アプリケーションを小さなまとまりに分割し、必要なコンポーネントだけサーバからロードしたい場合があるでしょう。Vue では、コンポーネント定義を非同期で解決するファクトリ関数としてコンポーネントを定義することができます。Vue は、コンポーネントをレンダリングする必要がある場合にのみファクトリ関数をトリガし、将来の再レンダリングのために結果をキャッシュします。例えば:

Vue.component('async-example', function (resolve, reject) {
  setTimeout(function () {
    // resolve コールバックにコンポーネント定義を渡します
    resolve({
      template: '<div>I am async!</div>'
    })
  }, 1000)
})

見ての通り、ファクトリ関数は、コンポーネント定義をサーバから取得したときに呼び出す必要がある resolve コールバックを受け取ります。 ここでの setTimeout はデモのためです。 コンポーネントの取得方法はあなた次第です。1つの推奨される方法は、 非同期コンポーネントと Webpack の code-splitting の機能 を使用することです:

Vue.component('async-webpack-example', function (resolve) {
  // この特別な require 構文は、ビルドされたコードを
  // 自動的に Ajax リクエストを介してロードされるバンドルに分割するよう Webpack に指示します
  require(['./my-async-component'], resolve)
})

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

Vue.component(
  'async-webpack-example',
  // `import` 関数は Promise を返します。
  () => import('./my-async-component')
)

ローカル登録 を使っている場合、Promise を返す関数を直接与えることもできます:

new Vue({
  // ...
  components: {
    'my-component': () => import('./my-async-component')
  }
})

もしあなたが Browserify ユーザで非同期コンポーネントを利用したいとしても、残念なことに作者は「非同期読み込みはBrowserifyがサポートするものではない」ことを明らかにしました、少なくとも公式では。Browserify のコミュニティーは既存の複雑なアプリケーションの役に立ちうる 回避策 を見つけました。その他の全ての場合、私たちはビルトインでファーストクラスの非同期サポートを理由に Webpack の利用を推奨します。

ロード状態のハンドリング

2.3.0 から新規

非同期コンポーネントのファクトリ関数は以下のような形のオブジェクトを返すこともできます:

const AsyncComponent = () => ({
  // ロードすべきコンポーネント (Promiseであるべき)
  component: import('./MyComponent.vue'),
  // 非同期コンポーネントのロード中に使うコンポーネント
  loading: LoadingComponent,
  // ロード失敗時に使うコンポーネント
  error: ErrorComponent,
  // loading コンポーネントが表示されるまでの遅延時間。 デフォルト: 200ms
  delay: 200,
  // timeout が設定され経過すると、error コンポーネントが表示されます。
  // デフォルト: Infinity
  timeout: 3000
})

上記の記法をルートコンポーネントに使用する場合、2.4.0 以上の Vue Router を使用しなければならないことに注意してください。