【Vue.js】slotについて

##親コンポーネントから子コンポーネントに、htmlごとデータを送る。

```App.vue
<template>
  <div>
    <likeHeader>
      <h1>{{ word }}</h1>
    </likeHeader>
  </div>
</template>

<script>
import likeHeader from "./components/likeHeader.vue";

export default {
  name: "App",
  data() {
    return {
      word: "スロットを通じて送られました",
    };
  },
  components: {
    likeHeader,
  },
};
</script>
```

```likeHeader.vue
<template>
  <div>
    <slot></slot>
  </div>
</template>

<script>
</script>
```
親コンポーネントのカスタムタグで囲まれた部分が、子コンポーネントのslotタグで囲まれた部分に送られる。

##slotのスコープについて

実際に記載しているファイルにスコープが発生している。例えば下記のように、親コンポーネントのカスタムタグに子コンポーネント定義しているchildを渡しても、読み込むことはできない。

```App.vue
<template>
  <div>
    <likeHeader>
      <h1>{{ child }}</h1>
    </likeHeader>
  </div>
</template>

<script>
import likeHeader from "./components/likeHeader.vue";

export default {
  components: {
    likeHeader,
  },
};
</script>
```

```likeHeader.vue
<template>
  <div>
    <slot></slot>
  </div>
</template>

<script>
export default {
  data() {
    return {
      child: "子で定義",
    };
  },
};
</script>
```

結果、未定義エラーとなる。

```
[Vue warn]: Property or method "child" is not defined on the instance 
but referenced during render. Make sure that this property is reactive, 
either in the data option, or for class-based components, 
by initializing the property. See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
```

ただし、styleタグについては、親コンポーネント子コンポーネントどちらに定義しても反映される。また、親と子に同一の定義があった場合は、

```App.vue
<style>
h1 {
  color: red;
}
</style>
```

```likeHeader.vue
<style>
h1 {
  color: blue;
}
</style>
```
親コンポーネントの定義が優先される。

##slotにデフォルトの値を設定する。

```likeHeader.vue
<slot>デフォルト</slot>
```
スロットタグの中に、デフォルトで表示したい値を設定する。

```App.vue
<template>
  <div>
    <likeHeader> </likeHeader>
  </div>

</template>
```
親コンポーネントのカスタムタグに値を入れなかった場合は、デフォルトの値が表示される。

![095becee7a6a6650a6332497b8b7f37b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547556/6945d33b-52c4-c5fc-393e-763e16f0d954.png)
逆に、カスタムタグの間に値を入れた場合は、デフォルトの値は表示されない。

```likeHeader.vue
<slot>デフォルト</slot>
```
スロットタグの中に、デフォルトで表示したい値を設定する。

```App.vue
<template>
  <div>
    <likeHeader>デフォルトじゃない</likeHeader>
  </div>

</template>
```
![60a9553803f6487980a39de29774a3dc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547556/4747e64c-efbe-20b3-b794-d449c127f976.png)

##名前付きslotで表示を分ける。

```App.vue
<template>
  <div>
    <likeHeader>
      <template v-slot:title>タイトル</template>
      <template v-slot:content>コンテンツ</template>
    </likeHeader>
  </div>
</template>

<script>
import likeHeader from "./components/likeHeader.vue";

export default {
  components: {
    likeHeader,
  },
};
</script>
```

```likeHeader.vue
<template>
  <div>
    <slot name="title"></slot>
    <slot name="content"></slot>
  </div>
</template>
```
親コンポーネントのカスタムタグ内にtemplateタグを設定し、v-slotディレクティブを設定し、任意の引数(title, content)を与える。
子コンポーネントで、slotタグに指定した任意の引数と一致する値(title, content)をname属性に与える。

##デフォルトslot
v-slotディレクティブを指定したtemplateタブで囲われいない部分は、全て子テンプレートのデフォルトslot(name属性が与えられていないslot)に集約されて表示される。

```App.vue
<template>
  <div>
    <likeHeader>
      <p>1</p>
      <template v-slot:title>タイトル</template>
      <p>2</p>
      <template v-slot:content>コンテンツ</template>
      <p>3</p>
    </likeHeader>
  </div>
</template>

<script>
import likeHeader from "./components/likeHeader.vue";

export default {
  components: {
    likeHeader,
  },
};
</script>
```

```likeHeader.vue
<template>
  <div>
    <slot name="title"></slot>
    <slot></slot>
    <slot name="content"></slot>
  </div>
</template>
```
![37fbad25f76dcb3bbdf61010be6483a4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547556/d5446632-17f4-467b-e1a7-4bc040b49a01.png)

この挙動は、親コンポーネント側で、templateタグに囲まれていいない部分を、新たにtemplateタグに集めて、そのtemplateタグにv-slot:default というディレクティブを与えている。
そして、子コンポーネントのデフォルトslotにname="default"を与えている。つまり下記のような処理をvueが自動で行っている。

```App.vue
<template>
  <div>
    <likeHeader>
      <template v-slot:default>
        <p>1</p>
        <p>2</p>
        <p>3</p>
      </template>
      <template v-slot:title>タイトル</template>
      <template v-slot:content>コンテンツ</template>
    </likeHeader>
  </div>
</template>

<script>
import likeHeader from "./components/likeHeader.vue";

export default {
  components: {
    likeHeader,
  },
};
</script>
```

```likeHeader.vue
<template>
  <div>
    <slot name="title"></slot>
    <slot name="default"></slot>
    <slot name="content"></slot>
  </div>
</template>
```
なお、soltに送る際は、templateタグでないとエラーとなる。(divタグとかではダメ)

##slotを用いて子コンポーネントから親コンポーネントにデーターを渡す。

slotにカスタム属性をバインドすることにより、親子コンポーネントのv-slotの仮引数に格納することができる。!

![IMG_BEDFBD4E6FC6-1.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547556/5f9d6c23-5e66-df5a-54a0-41f2e5c20c2b.jpeg)

![c31e9f693e229720c3f3f1cee9507b3b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547556/23eccddc-c5b1-74eb-34d2-e5cf5a15d76a.png)

なお、v-slot:は # に置き換えることができる。