スロットについて

スロットとは?

コンポーネント側で子コンポーネントの表示内容をガチャガチャやるための仕組み

名前付きスロット

コンポーネント
<template lang="pug">
div
  slot(name="header")
    p header(slotの中身はフォールバックコンテンツ・指定がなければこの内容を表示)
  slot(name="body")
    p body(slotの中身はフォールバックコンテンツ・指定がなければこの内容を表示)
  slot(name="footer")
    p footer(slotの中身はフォールバックコンテンツ・指定がなければこの内容を表示)
</template>
<script lang="ts">
import {defineComponent} from 'vue';

export default defineComponent({
  name: 'SlotChild',
});
</script>
コンポーネント
<template lang="pug">
div
  SlotChild
    template(v-slot:header)
      h1 「v-slot:」でスロットの名前を指定
    template(#body)
      h1 「v-slot:」は「#」に置き換え可能
</template>
<script lang="ts">
import {defineComponent} from 'vue';
import SlotChild from '@/components/SlotChild.vue';

export default defineComponent({
  name: 'SlotParent',
  components: {
    SlotChild,
  },
});
</script>

スコープ付きスロット

基本的には親コンポーネントから子コンポーネント側のデータにアクセスしようとすると、エラー(TypeError: _ctx.user is undefined)となる。

コンポーネント
<template lang="pug">
div
  slot(name="id")
    p {{user.id}}
  slot(name="name")
    p {{user.lastName}}
</template>
<script lang="ts">
import {defineComponent} from 'vue';

export default defineComponent({
  name: 'SlotChild',
  setup() {
    const user = {
      id: 100,
      firstName: 'Jack',
      lastName: 'Daniels',
    };
    return {
      user,
    };
  },
});
</script>
コンポーネント
<template lang="pug">
div
  SlotChild
    template(#name)
      // - "user"って誰やねんと怒られる
      h1 {{user.firstName}}
</template>
<script lang="ts">
import {defineComponent} from 'vue';
import SlotChild from '@/components/SlotChild.vue';

export default defineComponent({
  name: 'SlotParent',
  components: {
    SlotChild,
  },
});
</script>
そこで登場するのがスロットプロパティ

アクセスさせるにはまず子コンポーネント側でslot要素の属性としてデータをバインドする。 そして、親コンポーネント側で"v-slot"の値として名前を指定すると、その名前でバインドしたデータにアクセスできるようになる。 また、名前を指定する際に分割代入を使用することで直接バインドしたデータにアクセスすることもできる。

コンポーネント
<template lang="pug">
div
  slot(name="id")
    p {{user.id}}
  slot(name="name", :touchme="user")
    //- "touchme"という変数で"user"へのアクセスを許可する
    p {{user.lastName}}
</template>
<script lang="ts">
import {defineComponent} from 'vue';

export default defineComponent({
  name: 'SlotChild',
  setup() {
    const user = {
      id: 100,
      firstName: 'Jack',
      lastName: 'Daniels',
    };
    return {
      user,
    };
  },
});
</script>
コンポーネント
<template lang="pug">
div
  SlotChild
    template(v-slot:name="slotProps")
      // - "slotProps"がスロットプロパティです
      h1 {{slotProps.touchme.firstName}}
  SlotChild
    template(#name="{touchme}")
      // - 分割代入すれば直接"touchme"へアクセスできる
      h1 {{touchme.firstName}}
</template>
<script lang="ts">
import {defineComponent} from 'vue';
import SlotChild from '@/components/SlotChild.vue';

export default defineComponent({
  name: 'SlotParent',
  components: {
    SlotChild,
  },
});
</script>