VueJS 速習チュートリアル

Vue スコープ付きスロット

スコープ付きスロット(Scoped Slot)は、コンポーネント内のローカルデータを提供することで、親コンポーネント側でそのデータの表示(レンダリング)方法を自由に選択できるようにする機能です。

1. 親コンポーネントへのデータ送信

コンポーネントのスロット内で v-bind を使用することで、ローカルデータを親コンポーネントへ送信できます。

SlotComp.vue:

<template>
  <slot v-bind:lclData="data"></slot>
</template>

<script>
  export default {
    data() {
      return {
        data: 'これはローカルデータです'
      }
    }
  }
</script>

コンポーネント内部のデータは、このように v-bind で明示的に親へ送られない限り、親コンポーネントからはアクセスできないため「ローカル」と呼ばれます。

2. スコープ付きスロットからのデータ受信

子コンポーネントで v-bind を使って送られたデータは、親コンポーネント側で v-slot を使用して受け取ることができます。

2.1 実装例

App.vue:

<slot-comp v-slot="dataFromSlot">
  <h2>{{ dataFromSlot.lclData }}</h2>
</slot-comp>

上記の例において、dataFromSlot はスコープ付きスロットから受け取るデータオブジェクトを指す任意の名前です。スロットから送られた lclData プロパティにアクセスし、マスタッシュ構文(二重中括弧)を使用して <h2> タグ内にテキストをレンダリングしています。

3. 配列を使用したスコープ付きスロット

スコープ付きスロットは、v-for を使用して配列のデータを送信することも可能です。この場合も App.vue 側の基本構造は変わりません。

3.1 実装例

SlotComp.vue:

<template>
  <slot
    v-for="x in foods"
    :key="x"
    :foodName="x"
  ></slot>
</template>

<script>
  export default {
    data() {
      return {
        foods: ['リンゴ', 'ピザ', 'ご飯', '魚', 'ケーキ']
      }
    }
  }
</script>

App.vue:

<slot-comp v-slot="food">
  <h2>{{ food.foodName }}</h2>
</slot-comp>

4. オブジェクト配列を使用したスコープ付きスロット

スコープ付きスロットを使って、オブジェクトの配列データを v-for で処理して送信することもできます。

4.1 実装例

SlotComp.vue:

<template>
  <slot
    v-for="x in foods"
    :key="x.name"
    :foodName="x.name"
    :foodDesc="x.desc"
    :foodUrl="x.url"
  ></slot>
</template>

<script>
  export default {
    data() {
      return {
        foods: [
          { name: 'リンゴ', desc: 'リンゴは木になる果物の一種です。', url: 'img_apple.svg' },
          { name: 'ピザ', desc: 'ピザはパン生地にトマトソース、チーズ、具材をのせたものです。', url: 'img_pizza.svg' },
          { name: 'ご飯', desc: 'ご飯は人々によく食べられている穀物の一種です。', url: 'img_rice.svg' },
          { name: '魚', desc: '魚は水の中に住む動物です。', url: 'img_fish.svg' },
          { name: 'ケーキ', desc: 'ケーキは甘くて美味しいですが、健康食品とはみなされません。', url: 'img_cake.svg' }
       ]
      }
    }
  }
</script>

App.vue:

<slot-comp v-slot="food">
  <hr>
  <h2>{{ food.foodName }}<img :src="food.foodUrl"></h2>
  <p>{{ food.foodDesc }}</p>
</slot-comp>

5. スコープ付きスロットからの静的データ送信

スコープ付きスロットでは、Vue インスタンスの data プロパティに属さない「静的データ」を送ることもできます。
静的データを送る場合は v-bind を使いません。

以下の例では、違いがわかるように静的なテキストと、データインスタンスに動的にバインドされたテキストの両方を送信しています。

5.1 実装例

SlotComp.vue:

<template>
  <slot
    staticText="このテキストは静的です"
    :dynamicText="text"
  ></slot>
</template>

<script>
  export default {
    data() {
      return {
        text: 'このテキストは data プロパティ由来です'
      }
    }
  }
</script>

App.vue:

<slot-comp v-slot="texts">
  <h2>{{ texts.staticText }}</h2>
  <p>{{ texts.dynamicText }}</p>
</slot-comp>

6. 名前付きスコープ付きスロット

スコープ付きスロットに名前を付けることも可能です。
名前付きスコープ付きスロットを使用するには、コンポーネント内のスロットに name 属性を付与します。
親側でそのデータを受け取るには、v-slot ディレクティブ(または省略記法の #)でその名前を指定します。

6.1 実装例(コンポーネントを複数回呼び出す場合)

この例では、コンポーネントを "leftSlot" 用に1回、"rightSlot" 用に1回、計2回呼び出しています。

SlotComp.vue:

<template>
  <slot
    name="leftSlot"
    :text="leftText"
  ></slot>
  <slot
    name="rightSlot"
    :text="rightText"
  ></slot>
</template>

<script>
  export default {
    data() {
      return {
        leftText: 'このテキストは左側のスロットに属しています。',
        rightText: 'このテキストは右側のスロットに属しています。'
      }
    }
  }
</script>

App.vue:

<slot-comp #leftSlot="leftProps">
  <div>{{ leftProps.text }}</div>
</slot-comp>

<slot-comp #rightSlot="rightProps">
  <div>{{ rightProps.text }}</div>
</slot-comp>

6.2 実装例(一つのコンポーネント内で template タグを使い分ける場合)

あるいは、コンポーネントの呼び出しは1回にとどめ、内部で2つの異なる <template> タグを使用し、それぞれ別のスロットを参照することもできます。

App.vue:

<slot-comp>
  <template #leftSlot="leftProps">
    <div>{{ leftProps.text }}</div>
  </template>

  <template #rightSlot="rightProps">
    <div>{{ rightProps.text }}</div>
  </template>
</slot-comp>