dynamic-components について
Vueで実行時にコンポーネントを切り替える方法を扱うスキルです。タブやビューの構築に向け、切り替え対象の選択と状態の保持を説明します。
主な機能
- 01リアクティブな選択による切り替え
- 02コンポーネントマップでタブを構成
- 03KeepAliveによる状態保持
活用シーン
- 01ルーティングなしのタブ画面の構築
- 02入れ子のv-if/v-else描画の置き換え
- 03タブ切り替え時のカウンター維持
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ patternsdev/ skills --skill dynamic-componentsインストール後、Claude Code で/dynamic-componentsを入力して読み込まれたか確認します。
詳細
動的コンポーネント
目次
動的コンポーネントとは、予約済みの <component> 要素に is 属性をバインドすることで、コンポーネントを動的に変更(つまり、切り替え)できる機能です。
動的コンポーネントの仕組みを理解するために、例を見ていきましょう。Home、Feed、History という名前のコンポーネントがそれぞれあり、どのコンポーネントなのかを示すテキストだけを表示するとします。
使用する場面
- タブやビューなど、ユーザーの操作に応じて異なるコンポーネントを描画する必要がある場合に使用します
- 条件に応じてコンポーネントを描画するために、複数の
v-if/v-elseブロックを使う代替手段として役立ちます
手順
<component>のis属性に、コンポーネント定義を参照するリアクティブな値をバインドします- 名前とインポートしたコンポーネント定義を対応付ける
tabsオブジェクトを使用します - 動的コンポーネントの切り替え時に状態を保持するには、
<component>を<KeepAlive>で囲みます
詳細
<template><div class="tab">Home component</div></template>
<template><div class="tab">Feed component</div></template>
<template><div class="tab">History component</div></template>
クリックできるタブの一覧を表示するインターフェースを作成します。クリックされたタブに応じて、特定のコンポーネントを動的に描画します。
タブを切り替える際は、ルーティングを使わずにコンポーネントを動的にアンマウントおよびマウントします。v-if と v-else などのディレクティブを使い、条件に応じて子テンプレートを描画すれば同様のことを実現できますが、この用途には Vue の動的コンポーネントが最適です。
アプリの親 App コンポーネントでは、まず個々のコンポーネントを3つインポートし、テンプレートで使用できるようにします。また、初期値を "Home" に設定した currentTab というリアクティブプロパティも作成します。
<script setup>
import { ref } from "vue";
import Home from "./components/Home.vue";
import Feed from "./components/Feed.vue";
import History from "./components/History.vue";
const currentTab = ref("Home");
const tabs = {
Home,
Feed,
History,
};
</script>
tabs オブジェクトが参照しているのは、コンポーネント名だけではなく、実際のコンポーネント定義であることに注意してください。
App コンポーネントのテンプレートでは、表示する各コンポーネントに対応するタブボタンを3つ描画します。これを実現するために、v-for ディレクティブを使用します。
<template>
<div class="demo">
<button
v-for="(_, tab) in tabs"
:key="tab"
:class="['tab-button', { active: currentTab === tab }]"
@click="currentTab = tab"
>
{{ tab }}
</button>
</div>
</template>
特定の子コンポーネントを動的に描画するには、予約済みの <component> 要素に is 属性をバインドします。is 属性に設定する値は、動的に描画したい子コンポーネントに対応している必要があります。
<template>
<div class="demo">
<button
v-for="(_, tab) in tabs"
:key="tab"
:class="['tab-button', { active: currentTab === tab }]"
@click="currentTab = tab"
>
{{ tab }}
</button>
<component :is="tabs[currentTab]" class="tab"></component>
</div>
</template>
<script setup>
import { ref } from "vue";
import Home from "./components/Home.vue";
import Feed from "./components/Feed.vue";
import History from "./components/History.vue";
const currentTab = ref("Home");
const tabs = {
Home,
Feed,
History,
};
</script>
テンプレートに動的な <component /> 要素を配置すると、選択されているタブに応じて子コンポーネントが動的にアンマウントおよびマウントされるようになります。
状態の保持
動的コンポーネントを使用する際には、状態の保持が重要な検討事項となる場合があります。デフォルトでは、コンポーネントがアンマウントされると、その状態は失われます。ただし Vue では、<KeepAlive> コンポーネントを使って動的コンポーネントの状態を保持できます。
動的コンポーネントの状態を保持するには、<component> 要素を <KeepAlive> コンポーネントで囲みます。
<template>
<div class="demo">
<KeepAlive>
<component :is="tabs[currentTab]" class="tab"></component>
</KeepAlive>
</div>
</template>
<script setup>
// ...
</script>
<component> 要素を <KeepAlive> コンポーネントで囲むと、動的コンポーネントがアンマウントされても状態が保持されます。つまり、データやコンポーネントの状態が維持され、コンポーネントが再度マウントされたときにも以前の状態が保たれます。
例を確認するため、各子コンポーネントに、値が増加するシンプルなカウンターを追加します。
<template>
<div class="tab">
Home component
<p>Counter: {{ counter }}</p>
<button @click="incrementCounter">Increment</button>
</div>
</template>
<script setup>
import { ref } from "vue";
const counter = ref(0);
const incrementCounter = () => {
counter.value++;
};
</script>
これらの変更により、コンポーネントを動的に切り替えても、それぞれの子コンポーネントのカウンターの状態が保持されます。
<KeepAlive> コンポーネントを使うと、動的コンポーネントの状態を保持できるため、タブ切り替え時の動作を改善し、よりスムーズなユーザーエクスペリエンスを実現できます。