【Vue.js】名前付きslotを用いている時のデフォルトslotが適用される仕組み

## はじめに
仕事で使う事になったので1からVue.jsについて学んだ。
ちゃんと覚えておかないとまずそうな事を備忘録として1つ1つ残しておく。

## 名前付きslotを用いている時のデフォルトslotが適用される仕組み
### slotの基本
`<slot></slot>`を用いる事で親→子でhtmlの受け渡しを実現できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/ca29f6ce-ebbd-fa50-848b-81fc4dee72a6.png)

```vue:parent.vue
<template>
  <div>
    <LikeHeader>
      <h1>トータルのいいね数</h1>
      <h2>{{ number }}</h2>
    </LikeHeader>
  </div>
</template>

<script>
import LikeHeader from "./components/LikeHeader.vue";
export default {
  data() {
    return {
      number: 10,
    };
  },
  components: {
    LikeHeader,
  },
};
</script>
```
```vue:child.vue
<template>
  <div>
    <slot></slot>
  </div>
</template>
```

### デフォルトslot（フォールバックコンテンツ）
親コンポーネント側から何も`<slot></slot>`に渡ってこない場合に、デフォルトで何かを表示させる必要がある時にはデフォトslotを用いる。
デフォルトslotは`<slot></slot>`の中にhtmlを書くとそれがデフォルトslotになる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/78b9db26-fb27-614f-788d-1a1f07b6f781.png)

```vue:parent.vue
<template>
  <div>
    <LikeHeader>
      <!-- <h1>トータルのいいね数</h1> -->
      <!-- <h2>{{ number }}</h2> -->
    </LikeHeader>
  </div>
</template>

<script>
import LikeHeader from "./components/LikeHeader.vue";
export default {
  data() {
    return {
      number: 10,
    };
  },
  components: {
    LikeHeader,
  },
};
</script>
```
```vue:child.vue
<template>
  <div>
    <slot>
      <h2>デフォルトのタイトル</h2>
    </slot>
  </div>
</template>
```

### 名前付きslot
親コンポーネント側で複数のhtml要素を定義し、子コンポーネント側では指定のslotにそれを描画させたいという場合には`名前付きslot`を用いる。
これにより親コンポーネントで定義したhtml要素を意図した子コンポーネントのslotに渡すという複雑な実装が実現できる。
以下の実装では、

1. 　`<h1>トータルのいいね数</h1>`
1. 　`<h2>{{ number }}</h2>`

という2つのhtml要素をそれぞれ`title`・`number`という名前付きslotに渡している。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/fe8926a6-3f15-0ccf-f698-d9b0a9911530.png)

```vue:parent.vue
<template>
  <div>
    <LikeHeader headerText="hello">
      <template v-slot:title>
        <h1>トータルのいいね数</h1>
      </template>

      <template v-slot:number>
        <h2>{{ number }}</h2>
      </template>
    </LikeHeader>
  </div>
</template>

<script>
import LikeHeader from "./components/LikeHeader.vue";
export default {
  data() {
    return {
      number: 10,
    };
  },
  components: {
    LikeHeader,
  },
};
</script>
```
```vue:child.vue
<template>
  <div>
    <slot name="title"></slot>
    <hr />
    <slot name="number"></slot>
  </div>
</template>
```

名前付きslotを用いる場合、親コンポーネントでhtml要素を定義する際には、`<template v-slot:hogehoge></template>`のように`templateタグ`内にhtml要素を記述し、`v-slotディレクティブ`で名前[^1]を定義する必要がある。
受け取る子コンポーネント側では、`name属性`を記述しそこにv-slotディレクティブで定義した名前を代入する事でslotが機能する。

**※slotを用いる際には必ず`<template></template>`を用いないとエラーになるので注意。**

[^1]: これは`v-slotディレクティブ`の第1引数に値を渡す事で実現されている

### 名前付きslotを用いている時のデフォルトslotが適用される仕組み
名前付きslotを用いている場合のデフォルslotは以下のように実装できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/488b6388-5a59-42af-657b-8ab7ec8125e6.png)

```vue:parent.vue
<template>
  <div>
    <LikeHeader headerText="hello">
      <h2>みなさん</h2>

      <template v-slot:title>
        <h1>トータルのいいね数</h1>
      </template>

      <h4>いいねをたくさんもらいましょう</h4>
      <p>よろしくお願いします</p>

      <template v-slot:number>
        <h2>{{ number }}</h2>
      </template>
    </LikeHeader>
  </div>
</template>

<script>
import LikeHeader from "./components/LikeHeader.vue";
export default {
  data() {
    return {
      number: 10,
    };
  },
  components: {
    LikeHeader,
  },
};
</script>
```
```vue:child.vue
<template>
  <div>
    <slot name="title"></slot>
    <hr />
    <slot></slot>
    <hr />
    <slot name="number"></slot>
  </div>
</template>
```

親コンポーネントで`<template></template>`で囲われていない部分がデフォルslotに渡るhtml要素であり、子コンポーネントで`<slot></slot>`と記載している部分がデフォルトslot（フォールバックコンテンツ）になる。

ここで、このデフォルトslotがどのように実現されているか？を詳しく見ていく。
実は、親コンポーネントで`<template></template>`で囲われていない部分は、Vue.js側で以下のように解釈されて`<template></template>`に囲われているものとして扱われる。この時、v-slotディレクティブも自動的に付与されるが`v-slot:default`がそれ。

```
<template v-slot:default>
  <h2>みなさん</h2>
  <h4>いいねをたくさんもらいましょう</h4>
  <p>よろしくお願いします</p>
</template>
```

そして実は子コンポーネントの`<slot></slot>`もVue.js側で以下のように解釈され、`default`という名前の名前付きslotに置き換えられている。

```
<slot name="default"></slot>
```

そのため、以下のようなソースコードでも上記の図と同じ結果になる。

1. 　子コンポーネントの`<slot></slot>`をわざと`<slot name="default"></slot>`にする（親コンポーネントは変えないで）
1. 　親コンポーネントのデフォルトのhtml要素を`<template v-slot:default></template>`内にわざと記述する（子コンポーネントは変えないで）
1. 　上記1・2の親・子それぞれの変更を行う

※このような仕組みなので名前付きslotの名前を`default`にしてしまうと意図しない動きになるので注意が必要。

| # | 図 |
|---|:-:|
| 子コンポーネントで`name="title"`を`name="default"`にする | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/c9bfc0ff-67d5-feb4-0cff-1c8187889ba8.png) |
| 親コンポーネントで`<template v-slot:title>`を`<template v-slot:default>`にする  | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/33367e6e-8031-f447-5828-a2988ef9a3c3.png) |

## Vue.jsの勉強メモ一覧記事へのリンク
Vue.jsについて勉強した際に書いた勉強メモ記事のリンクを集約した記事。

- https://qiita.com/yuta-katayama-23/items/dabefb59d16a83f1a1d4