Written by TSUYOSHI

【Vue】slotの使い方 【名前付きスロット・スロットプロパティ・省略記法】

JavaScript PROGRAMMING Vue

本記事では、Vueのslot機能を用いてコンポーネントへデータを受け渡す活用方法について詳細まで解説します。

Vueでよく使うslotですが、書き方や機能が色々あって混乱する方は多いのではないでしょうか。僕もVueの学習初期の頃は混乱していてしまったので、同じ失敗をする方が減って欲しいと思い、slotの使い方について基礎〜色々な書き方について、ご紹介していきます。

この記事を書いている僕は、フロントエンドのプログラマー(フリーランス)として、これまで4年ほどのWeb制作経験があり、Vue,React,Nuxtなどフロントエンドの開発を得意としています。

この記事を読むことによって、Vueでslotの使い方が分かるようになります。

【Vue】slotの使い方 【名前付きスロット・スロットプロパティ・省略記法】

slotは色々な書き方があって混乱しやすい

slotは色々な書き方があって混乱することが多いと思います。例えば以下のように書き方が色々あります。

・名前を省略したdefaultのslot
・名前付きslot
・slotを使った子コンポーネント→親コンポーネントへ値の受け渡し
・slotの省略表記

子→親コンポーネントの値の受け渡しや省略記法が多く存在することが、slotを使う際によく混乱する部分だと思います。この記事では、このあたりを細かく解説するので、何となくしかわかっていない方もslotがよく分かり、使いこなせるようになるはずです。

それでは順番にslotの使い方について、解説していきます。

GitHubのコード

記事中に紹介しているサンプルコードは、GitHubにも上げているので以下から確認ができます。
https://github.com/it-web-life/vue_slot_usage

slotの基本的な使い方

Vueでは機能ごとなどで、コンポーネントに分けてパーツを作っていき、親コンポーネントから子コンポーネントへ値を受け渡すことが多いです。(例:サンプルコード:# Example1)
この時にhtmlのテンプレート表示を丸ごと、子コンポーネントに渡したい時などにslotという便利な機能が使えます。

slotでテンプレートのデータごと受け取って表示する

コンポーネントでは、<slot></slot>で親コンポーネントからテンプレートごとデータを受け取って表示することができます。

» サンプルコード:# Example2

▼親コンポーネントのコード例

<template>
  <div class="block">
    <ExampleChild>
      <h3>Example2: slotでテンプレート自体を渡す方法</h3>
      <p>こんにちは、山田さん</p>
    </ExampleChild>
  </div>
</template>

▼子コンポーネントのコード例

<template>
  <div class="block">
    <slot></slot>
  </div>
</template>

この時、CSSのスコープは親コンポーネントで書いても、子コンポーネント側でもどちらでも書くことが可能です。

slotで渡す時に何もない時のフォールバック

slotで何もデータが渡されない時に表示するデフォルトの表示データ(フォールバックコンテンツ)を設定することが可能です。

» サンプルコード:# Example2 の ExampleChild.vue

<template>
  <div class="block">
    <slot>
      <p>デフォルトの表示です</p>
    </slot>
  </div>
</template>

slotで渡す時になにもない時のフォールバックとして、子コンポーネントのslotタグ内に書いたものがデフォルト値となります。

名前付きslot(v-slot)を使ってslotを複数切り分けて表示する

名前付きslotを使う

親コンポーネントから子コンポーネントにslotで値を渡す際に、「名前付きスロット」を使って複数のslotを使うことが可能です。

親コンポーネントではv-slotを使用して「<template v-slot:xxxxx> 〜 </template>」のように指定し、子コンポーネントではname属性を指定して「<slot name="xxxxx"></slot>」のようにします。

この時の注意点としては、v-slotを付けるタグはtemplateタグのみ許されているので、例えばdivタグなどにすることはできません。

» サンプルコード:# Example3

▼親コンポーネントのコード例

<template>
  <div class="block">
    <ExampleChild>
      <template v-slot:title>
        <h3>Example3: 名前付きslotで値を渡す方法</h3>
      </template>
      <template v-slot:description>
        <p>こんにちは、山田さん</p>
      </template>
    </ExampleChild>
  </div>
</template>

▼子コンポーネントのコード例

<template>
  <div class="block">
    <slot name="title"></slot>
    <p>v-slotを使って名前付きコンポーネントを使用しています</p>
    <slot name="description"></slot>
  </div>
</template>

slotのdefaultテンプレート(nameがないtemplate)

子コンポーネントにおいて、「name=”default”」は省略が可能で、以下の2つは同じ意味になります。
<slot name="default"></slot>
 ↓ ※「name=”default”」は省略が可能
<slot></slot>

また親コンポーネントにおいて、name指定がないslot部分はすべてまとめてdefaultのslot部分に表示されるようになっています。

» サンプルコード:# Example4

▼親コンポーネントのコード例

<template>
  <div class="block">
    <ExampleChild>
      <h4>デフォルトのパーツ1</h4>
      <template v-slot:title>
        <h3>Example4: 名前付きslotのname="default"の使い方(name="default"は省略が可能)</h3>
      </template>
      <div><span>デフォルトのパーツ2</span></div>
      <template v-slot:description>
        <p>こんにちは、山田さん</p>
      </template>
      <p>デフォルトのパーツ3</p>
      <p>name="default"は省略が可能。名前付きslot以外のslot部分がまとめて結合されてdefaultのslot部分に表示されます。</p>
    </ExampleChild>
  </div>
</template>

▼子コンポーネントのコード例

<template>
  <div class="block">
    <slot name="title"></slot>
    <slot name="description"></slot>
    <slot></slot>
  </div>
</template>

以下のような表示になります。

【スロットプロパティ】子コンポーネントの値を親コンポーネントに共有する方法

子コンポーネントの値を親コンポーネントに共有して,親コンポーネントで使う方法

slotを使っている時に、子コンポーネントで使っている値を親コンポーネントに渡して使いたい場合、「スロットプロパティ」を使うと対応できます。

▼スロットプロパティの使い方

  • 子コンポーネントのslotタグv-bind属性を付ける。
    <slot name="fuga" :text="hoge"></slot>
  • 親コンポーネントではv-slotにプロパティを持つことによって、値の受け取りができる。
    <template v-slot:fuga="slotProps">
      <h2>こんにちは</h2>
      <p>{{ slotProps.text }}</p>
    </template>

    ※「slotProps」と定義している部分の名前は何でもよく、他の名前にできます。

具体的なスロットプロパティを使った例

» サンプルコード:# Example5

▼親コンポーネントのコード例

<template>
  <div class="block">
    <ExampleChild>
      <template v-slot:example="slotProps">
        <h3>Example5: スロットプロパティを使って、子→親に値を渡す方法</h3>
        <p>こんにちは、{{ slotProps.user.lastName }} {{ slotProps.user.firstName }}さん</p>
        <p>年齢:{{ slotProps.age }}歳</p>
      </template>
    </ExampleChild>
  </div>
</template>

▼子コンポーネントのコード例

<template>
  <div class="block">
    <slot name="example" :user="user" :age="age"></slot>
  </div>
</template>

<script>
export default {
  name: 'ExampleChild',
  data() {
    return {
      user: {
        firstName: '太郎',
        lastName: '山田'
      },
      age: 20
    }
  }
}
</script>

▼表示例

defaultのslotしかない場合は省略した書き方が可能

v-slotで名前付きスロット(name="xxx")を使わず、defaultslotしかない場合はスロットプロパティにおいて省略した書き方が可能になります。具体的には以下のような例で、TextBlockコンポーネントからスロットプロパティでtextbindして渡された場合の書き方です。

<TextBlock>
  <template v-slot:default="slotProps">
    <h2>こんにちは</h2>
    <p>{{ slotProps.text }}</p>
  </template>
</TextBlock>

 ↓ ※同じこと (templateタグは省略可能)

▼templateタグを省略して親コンポーネントにv-slotを記載できる

<TextBlock v-slot:default="slotProps">
  <h2>こんにちは</h2>
  <p>{{ slotProps.text }}</p>
</TextBlock>

 ↓ ※同じこと (:defaultは省略可能)

▼defaultはもともと省略して書ける

<TextBlock v-slot="slotProps">
  <h2>こんにちは</h2>
  <p>{{ slotProps.text }}</p>
</TextBlock>

ちなみに分割代入で値を受け取ることも可能です。

slotPropsとして受け取った場合

<TextBlock v-slot="slotProps">
  <h2>こんにちは</h2>
  <p>{{ slotProps.text }}</p>
</TextBlock>

↓ ※同じこと

{ text }としてプロパティを分割代入で直接受け取った場合

<TextBlock v-slot="{ text }">
  <h2>こんにちは</h2>
  <p>{{ text }}</p>
</TextBlock>

» サンプルコード:# Example6

v-slotは動的に変化させることが可能

以下のような形で[]内に変数で動的にv-slotを変えることができます。

<template v-slot:[title]></template>
export default {
  data() {
    return {
      title: 'title',
    }
  }
}

» サンプルコード:# Example7

▼親コンポーネントのコード例

<template>
  <div class="block">
    <ExampleChild>
      <template v-slot:[example]>
        <h3>Example7: slot名を動的に変化させる</h3>
      </template>
    </ExampleChild>
  </div>
</template>

<script>
import ExampleChild from './ExampleChild.vue'

export default {
  name: 'Example',
  components: {
    ExampleChild
  },
  data() {
    return {
      example: 'title'
    }
  }
}
</script>

▼子コンポーネントのコード例

<template>
  <div class="block">
    <slot name="title"></slot>
    <p>[]を使ってslot名を動的に変化させることが可能です。</p>
  </div>
</template>

v-slot は #に置き換えた省略表記が可能

v-slotは「#」で代替して短く表記することが可能になっています。

<template v-slot:fuga>
  <h2>こんにちは</h2>
</template>

 ↓ ※同じこと

<template #fuga>
  <h2>こんにちは</h2>
</template>

defaultで#使う時の書き方

defaultのslotでは省略ができないので、#defaultと記載するようにします。defaultを書かないと「#="slotProps"」のように、意味がわからない形になってしまうためです。

<TextBlock v-slot="slotProps">
  <h2>こんにちは</h2>
  <p>{{ slotProps.text }}</p>
</TextBlock>

 ↓ ※同じこと

<TextBlock #default="slotProps">
  <h2>こんにちは</h2>
  <p>{{ slotProps.text }}</p>
</TextBlock>

» サンプルコード:# Example8

まとめ

slotの書き方について解説しました。

slotを使う際には、defaultのslot・名前付きslot・スロットプロパティ・動的slot・省略記法など、各々を理解していないと既存のVueソースコードを読む際などは特に混乱しがちです。それぞれの書き方の基本を覚えておけば混乱せず、視認性のよいコードを書けるようになります。

また別記事の「Vue・Nuxtでslotの中身の有無で処理を分ける方法 【$scopedSlotsを使います】」で、slot内でslot指定がされているかどうかでv-ifによる表示の出し分けについても解説しているので、よろしければ参考にしてみてください。

ご参考になれば幸いです。

※当サイトでは一部のリンクについてアフィリエイトプログラムを利用して商品を紹介しています