Future Tech Blog
フューチャー技術ブログ

Vue.jsのslotの機能を初心者にわかるように解説してみた


フロントエンド記事集中投稿ウィークの1本目です。

はじめに

はじめまして。2017年入社の永井です。

東郷さんによる「Vue.js最初の難関、「props down, event up」を初心者にわかるように解説してみた」はご覧になられましたでしょうか?おそらくVue.js最初の難関は無事乗り越えられたと思います!

今回は「Vue.js初心者にわかるように解説してみた」第二弾として、props down,event up と同様に親子関係が肝となる、「slot」について、初心者に向けて説明していきたいと思います。

slotとは

slotとは親となるコンポーネント側から、子のコンポーネントのテンプレートの一部を差し込む機能 です。

  • スロットというと「スロットマシン」が思い浮かびますが、もともとslotの「差し込み口」という意味から派生して、コインの投入口があるスロットマシンの意味をもつようになったそうです。

以下では大きく3つ、デフォルトのslot、名前付きslot、スコープ付きslotについて、説明していきます。

以下ではVue Cliを利用します。インストールしたときに作られているAbout.vue上で挙動を試す、擬似ハンズオン形式で説明していきます。

デフォルトslot

コンポーネントの復習

/src/components 配下にとても簡単なコンポーネントを作成します。

myCom.vue
1
2
3
4
5
6
7
<template>
<div class="mycom">
<p>name: Mirai Taro<p>
</div>
</template>
<style>
</style>

/src/views/About.vue 上で、作ったコンポーネントを呼び出します。
以下のソースでは、<MyCom> </MyCom> のタグでコンポーネントを利用します。ここで <MyCom>タグに囲まれた部分が無視されていることを確認してください。下記ではそれを試すために、「未来太郎」と記述してみます。

About.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
<div class="home">
<MyCom>未来太郎</MyCom>
</div>
</template>
<script>
import MyCom from '../components/MyCom.vue'
export default {
components: {
MyCom
}
}
</script>

💻画面の出力結果:

About.vue上で「未来太郎」と記述した部分は出力されません。

slot

コンポーネント側のテンプレートに<slot>タグを記述すると、その場所ではスロットコンテンツが埋め込まれます。

myCom.vue
1
2
3
4
5
6
7
<template>
<div class="mycom">
<p>name:<slot>Mirai Taro</slot></p>
</div>
</template>
<style>
</style>

親側でスロットコンテンツが定義されていた場合は、<slot>タグで囲まれたコンポーネント側のコンテンツは表示されず、親側のスロットコンテンツが表示されます。今回の例ではAbout.vue上の「未来太郎」が表示されていることを確認してください。

💻画面の出力結果:

名前付きslot

複数のslotを利用したい場合には、異なる名前をつけて利用します。

Vue 2.6.0以降は、コンポーネント側では、<slot name = "XXX">で名前付きslotを定義します。

myCom.vue
1
2
3
4
5
6
7
8
<template>
<div class="mycom">
<p>name:<slot name="nm">Mirai Taro</slot></p>
<p>address:<slot name="add">Osaki</slot></p>
</div>
</template>
<style>
</style>

親側では、v-slot:XXXの形で、コンポーネントのタグ内で差し込みたい名前付きslotを指定することができます。

About.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div class="home">
<MyCom></MyCom>
<MyCom>
<template v-slot:nm>未来太郎</template>
</MyCom>
<MyCom>
<template v-slot:add>渋谷</template>
</MyCom>
</div>
</template>
<script>
import MyCom from '../components/MyCom.vue'
export default {
components: {
MyCom
}
}
</script>

上記ソースでは、1行目がコンポーネント通りに出力、2行目は、氏名が親側のコンテンツに、3行目は住所が親側のコンテンツに置き換わっていることを確認してください。

💻画面の出力結果:

名前つきslotの省略記法

v-bind を 「:」、v-onを「@」で省略できるように、v-slotも「#」で省略して記述することができます。
上記のAbout.vueのソースは以下のように書いてもよいです。

About.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div class="home">
<MyCom></MyCom>
<MyCom>
<template #nm>未来太郎</template>
</MyCom>
<MyCom>
<template #add>渋谷</template>
</MyCom>
</div>
</template>
<script>
import MyCom from '../components/MyCom.vue'
export default {
components: {
MyCom
}
}
</script>

スコープ付きslot

スコープ付きslotを利用することで、子コンポーネントから親コンポーネントに対して、スロットコンテンツの定義に必要なデータを受け取ることが可能です。

  • 実務上では、子から親へデータを渡したい時はVuex使ったほうが良い場面が多いかと思いますが、今回はslotの説明の記事なので触れることとします。

こちらもvue 2.6.0より推奨されている書き方が変わっています。

うまくいかないパターン

以下のようにコンポーネントを準備します。

MyCom.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div class="mycom">
<p>name:<slot>{{ userNm.enName }}</slot></p>
</div>
</template>
<script>
export default {
name: 'MyCom',
data () {
return {
userNm: {
enName: 'Mirai Taro',
jpName: '未来太郎' // ←slot内で参照したいデータ
}
}
}
}
</script>
<style>
</style>

この時、以下のようにして、jpNameを親側から呼び出すことはできません。

About.vue(アンチケース)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<div class="home">
<MyCom>
{{ userNm.jpName }}
</MyCom>
</div>
</template>
<script>
import MyCom from '../components/MyCom.vue'
export default {
components: {
MyCom
}
}
</script>

スコープ付きslotの記述方法

スコープ付きslotを利用するには、子コンポーネント側では、<slot>タグに対してv-bindを行います。

MyCom.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div class="mycom">
<p>name:<slot :userNm="userNm">{{ useNm.enName }}</slot></p>
</div>
</template>
<script>
export default {
name: 'MyCom',
data () {
return {
userNm: {
enName: 'Mirai Taro',
jpName: '未来太郎' // ←slot内で参照したいデータ
}
}
}
}
</script>
<style>
</style>

親側では<v-slot:default>で受け取ることで、子コンポーネントのjpNameの値をとることができます。<v-slot:default="slotProps">のslotPropsは任意ですので重複がなければ、どんな文字列でも構いません。(公式ドキュメントに合わせました。)

About.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<div class="home">
<MyCom v-slot:default="slotProps">
{{ slotProps.userNm.jpName }}
</MyCom>
</div>
</template>
<script>
import MyCom from '../components/MyCom.vue'
export default {
components: {
MyCom
}
}
</script>

なお、名前付きslotを併用する場合、defaultの部分はそれぞれのslotの名前となります。

  • slotに名前が設定されていない時のデフォルトの名前がdefaultになるということです。

💻画面の出力結果:

無事、子コンポーネント側のjpNameの値を画面に出力することができました!

終わりに

slotの機能はドキュメントなどを読んでもいまいちピンとこなかったり、2.6.0以降で推奨される書き方が変わったりで、個人的には学習に苦労してしまいました。
もし同様に苦しんでいる方がいらっしゃいましたら、今回の擬似ハンズオンでイメージがつかめたら幸いです。

雑談

  • お気づきかも知れませんが、「未来太郎」さんはフューチャー社内でよく記入例に使われる名前です。
  • 例に出てきた「Osaki(大崎)」はフューチャーの所在地です。現在、大崎アートヴィレッジやthinkparkに入居しています。こんなオフィスです。(フューチャーの採用ページのwork placeのページに飛びます)
  • 例に出てきた「渋谷」は2006年ごろ(フューチャーシステムコンサルティング時代)までの所在地です。こんなオフィスだったそうです。(外部リンク)

関連記事: