公式 | https://jp.vuejs.org/v2/guide/components-slots.html |
vue.js | 2.6.10 |
slot
を使うとコンテンツ配信の受け口として利用出来る。
// Vue コンポーネント my-content 作成
Vue.component('my-content', {
template : `
<div>
<slot></slot>
</div>
`
})
<div id="app">
<my-content>
hoge
</my-content>
</div>
上記でいうと hoge
というテキストコンテンツが スロットコンテンツ と呼ばれる。
Vue.component('my-content', {
render (h) {
return h('div', this.$slots.default)
}
})
this.$slots
は配信されたコンテンツが入ってる。
<slot>
には name
を付けれるが( 名前付きスロット )、名前が付いていない <slot>
は暗黙的に default という名前になる。
this.$slots.default
は後述
いわゆるデフォルトコンテンツ
Vue.component('my-content', {
template : `
<div>
<slot>default text</slot>
</div>
`
})
<div id="app">
<my-content></my-content>
</div>
Vue.component('my-content', {
template : `
<div>
<slot name="header"></slot>
<p>ヘッダとフッタの間</p>
<slot name="footer"></slot>
</div>
`
})
<div id="app">
<my-content>
<template v-slot:header>
header text
</template>
<template v-slot:footer>
footer text
</template>
</my-content>
</div>
<slot>
に対して name
属性を指定し、配信するコンテンツを v-slot:[name]
属性を持つ <template>
タグで囲むと対応する slot
に配信される。
<template>
タグに v-slot
ディレクティブがなかったり、そもそも <template>
に囲まれていないコンテンツはデフォルトスロットのコンテンツと見なされる。
上記の話を言い換えると、スロットコンテンツがデフォルトスロットだけの場合 <template>
を省略できるという意味になる。
<my-content>
<template v-slot:default>
text
</template>
</my-content>
↓↓↓↓↓↓
<my-content v-slot:default><!-- コンポーネントに直接ディレクティブを指定 -->
text
</my-content>
↓↓↓↓↓↓
<my-content><!-- デフォルトスロットだけなのでディレクティブすら省略 -->
text
</my-content>
また、v-slot
ディレクテイブは #
と省略することができる。
※ 省略した場合、スロット名は必須になるので、正確には v-slot:
を省略したもの
<div id="app">
<my-content>
<template #header>
header text
</template>
<template #header>
footer text
</template>
</my-content>
</div>
ただし省略するとデフォルトスロットであってもスロット名は必須になる。
以下は ReferenceError: user is not defined
というエラーが出る。
// Vue コンポーネント my-content 作成
Vue.component('my-content', {
data () {
return {
user : 'yokota'
}
},
template : `
<div>
<slot></slot>
</div>
`
})
<div id="app">
<my-content>
{{ user }}
</my-content>
</div>
テンプレートにはスコープがあり、上記スロットコンテンツ
<my-content>
{{ user }}
</my-content>
は親テンプレート(my-contentコンポーネントを呼び出しているテンプレート)のスコープであり
my-contentコンポーネントの data.user
にはアクセスできない。
Vue.component('my-content', {
data () {
return {
user : 'yokota'
}
},
template : `
<div>
<slot v-bind:user="user"></slot>
</div>
`
})
<slot>
タグに v-bind
ディレクティブで data
のプロパティ(上では user
)をバインドしてやると、スロットコンテンツでもプロパティの値を受け取ることが出来る。
バインドされた属性を スロットプロパティ という。
スロットプロパティの受け取り方は v-slot
ディレクティブの値としてスロットプロパティ受け取り窓口になる名前を指定してやる。
<div id="app">
<my-content v-slot:default="slotProps">
{{ slotProps.user }}
</my-content>
</div>
上記の例だと slotProps.user
で my-content
コンポーネントの <slot>
タグでバインドされたスロットプロパティ user
にアクセスできる。
スコープ付きスロットは内部的にはスロットコンテンツを単一引数の関数で囲むことで動作させているようなので分割代入が行える。
// quote : https://jp.vuejs.org/v2/guide/components-slots.html
function (slotProps) {
// ... slot content ...
}
つまり以下のような書き方ができる。
<div id="app">
<my-content v-slot:default="{ user }">
{{ user }}
</my-content>
</div>
これは内部的に
function (slotProps) {
// ... slot content ...
}
// ↓↓↓
function ({ user }) {
// ... slot content ...
}
のイメージ。つまり以下のようなことが出来る。
<!-- 例1:別名をつける -->
<div id="app">
<my-content v-slot:default="{ user : name }">
{{ name }}
</my-content>
</div>
<!-- 例2:フォールバックを定義 -->
<div id="app">
<my-content v-slot:default="{ sex = 'male' }">
{{ sex }}
</my-content>
</div>