1. slot-scopeとは何か?
slot-scopeはVue 2におけるスコープ付きスロットの構文です。その役割は子コンポーネントがデータを親コンポーネントに渡し、親コンポーネントがそのデータに基づいてカスタムのレンダリング内容を決定できるようにすることです。
簡単に言うと:
- 子コンポーネント:データを提供する役割
- 親コンポーネント:データに基づいてレンダリング方法を決定する役割
2. なぜslot-scopeが必要なのか?
次のようなシナリオを想像してみてください:
- リストコンポーネント(子コンポーネント)をラップしており、このコンポーネントは配列(例えばitems)を受け取り、v-forで各項目をレンダリングします。
- しかし、リストコンポーネントは各項目データがどのようにレンダリングされるべきかを知りません。なぜなら、レンダリングロジックはビジネス要件によって異なる可能性があるからです。
- この場合、各項目データ(例えばitem)を親コンポーネントに渡し、親コンポーネントがレンダリング方法を決定する必要があります。
これがslot-scopeの役割です:子コンポーネントがデータを提供し、親コンポーネントがレンダリング方法を決定する。
3. コード例で理解する
子コンポーネント(itemList.vue)
<template>
<ul>
<li v-for="item in items" :key="item.id">
<!-- slotを使ってitemデータを親コンポーネントに渡す -->
<slot :item="item"></slot>
</li>
</ul>
</template>
<script>
export default {
props: {
items: {
type: Array,
required: true,
},
},
};
</script>
子コンポーネントはitems配列を受け取り、v-forで各項目をレンダリングします。ループ内では、子コンポーネントが<slot :item="item"></slot>を使って各itemデータを親コンポーネントに渡します。
親コンポーネント(App.vue)
<template>
<item-list :items="items">
<!-- slot-scopeで子コンポーネントから渡されたitemデータを受け取る -->
<template slot-scope="slotProps">
<span v-if="slotProps.item.isCompleted">✓</span>
<span>{{ slotProps.item.name }}</span>
</template>
</item-list>
</template>
<script>
import itemList from "./itemList";
export default {
data() {
return {
items: [
{ id: 1, name: "タスク1", isCompleted: false },
{ id: 2, name: "タスク2", isCompleted: true },
{ id: 3, name: "タスク3", isCompleted: false },
],
};
},
components: {
itemList,
},
};
</script>
親コンポーネントは:items="items"でitems配列を子コンポーネントに渡します。子コンポーネントがslotを使って各itemデータを返し、親コンポーネントがslot-scope="slotProps"でitemデータを受け取り、item.isCompletedとitem.nameに基づいてレンダリング内容をカスタマイズします。
4. データの流れ
- 親コンポーネントがitems配列を子コンポーネントに渡します。
- 子コンポーネントがv-forでitemsをループし、各itemデータをslotを通じて親コンポーネントに返します。
- 親コンポーネントがslot-scopeでitemデータを受け取り、レンダリング方法を決定します。
5. なぜ$emitやvuexを使わないのか?
- $emit:$emitはイベントをトリガーするために使われます。特定のタイミング(例えばボタンクリック)でデータを渡すのに適しています。しかし、リストレンダリングのシナリオでは、v-forループの各項目にデータを渡す必要があるため、$emitではこの要件を満たせません。
- vuex:vuexはグローバル状態管理ツールで、コンポーネント間でデータを共有するのに適しています。しかし、この親子コンポーネント間の通信シナリオでは、vuexを使うと過度に複雑になり、必要ありません。
slot-scopeはこのシナリオ専用に設計されています:子コンポーネントがデータを提供し、親コンポーネントがレンダリング方法を決定する。
6. 実際の応用シナリオ
- テーブルコンポーネント:例えばAnt DesignのTableコンポーネントでは、各行データが親コンポーネントに渡され、親コンポーネントが各行のレンダリング方法をカスタマイズできます。
- リストコンポーネント:汎用的なリストコンポーネントをラップし、親コンポーネントが各項目データに基づいてレンダリング内容をカスタマイズできます。
- フォームコンポーネント:フォームコンポーネントをラップし、親コンポーネントが各フォームフィールドのデータに基づいてレンダリング方法をカスタマイズできます。
7. まとめ
- slot-scopeの役割:子コンポーネントがデータを親コンポーネントに渡し、親コンポーネントがそのデータに基づいてカスタムのレンダリング内容を決定できるようにする。
- 使用シナリオ:子コンポーネントがデータをレンダリングする必要があるが、親コンポーネントが具体的なレンダリング方法を制御する必要がある場合。
- 利点:柔軟性、結合度の低さ、再利用性の高さ。
8. Vue 3での代替案
Vue 3では、slot-scopeは廃止され、代わりにv-slot構文が使われます。例:
<template v-slot:default="slotProps">
<span v-if="slotProps.item.isCompleted">✓</span>
<span>{{ slotProps.item.name }}</span>
</template>
または省略形:
<template #default="{ item }">
<span v-if="item.isCompleted">✓</span>
<span>{{ item.name }}</span>
</template>