组件插槽
如下代码所示:
Vue.component('fun-div', {
template: `
<div>
<strong>before</strong>
<slot></slot>
<strong>after</strong>
</div>
`
})
上面组件的模板中,使用 <slot>
标签声明了一个插槽,当使用 <fun-div>
标签时,自动将标签中的内容添加到 <slot>
的位置。如下 HTML 代码:
<div class="container">
<fun-div>
这是中间的内容
</fun-div>
</div>
结果:
实际上,在 <fun-div>
标签中可以插入任何内容,也可以是其他组件、模板内容。比如插入一个 input
标签:
<div class="container">
<fun-div>
<input type="text">
这是中间的内容
</fun-div>
</div>
结果:
还可以为插槽定义默认内容,当没有内容填充插槽时,将显示默认内容,如下代码所示:
Vue.component('fun-div', {
template: `
<div>
<strong>before</strong>
<slot>这是默认内容</slot>
<strong>after</strong>
</div>
`
})
<div class="container">
<fun-div>这是中间的内容</fun-div>
<fun-div></fun-div>
</div>
结果:
具名插槽
在插槽 <slot>
标签中设置 name
属性可以分辨 <slot>
标签,也就是可以设置多个插槽,如下代码所示:
Vue.component('fun-div', {
// 具名插槽的用法就是使用 name 属性标识出 slot 标签的名称,
// 在使用标签插入内容时可以使用 slot 属性的属性值来分辨插槽
template: `
<div>
<slot name="header"></slot>
<slot></slot>
<slot name="footer"></slot>
</div>`
})
上面代码中第 6 行使用 name
将 <slot>
进行区分,在插入内容时,可以使用 slot
属性为具体的 <slot>
插槽插入内容。如下代码所示:
<div class="container">
<fun-div>
<p slot="header">这是 header 插槽中的内容</p>
<p slot="footer">这是 footer 插槽中的内容</p>
<p>普通插槽</p>
</fun-div>
</div>
上面代码中第 3 行,设置 slot="header"
在 JS 部分的 name="header"
插槽中插入内容。
结果:
通常在 HTML 中使用 <template>
标签在一个具名插槽中插入多个 html 标签,而 <template>
标签不在 HTML 结构中显示,如下代码所示:
<fun-div>
<template slot="header">
<div>这是 header 标签中插入的第一个 div</div>
<input type="text" value="header">
</template>
<template slot="footer">
<div>这是 header 标签中插入的第一个 div</div>
<input type="text" value="footer">
</template>
<p>普通插槽</p>
</fun-div>
结果:
作用域插槽
可以在子组件中使用 v-bind
指令设置属性,属性值为要传递的数据。在父组件中使用 <template slot-scope="slotProps"> </template>
将子组件传递的数据保存到 slotProps
对象中。
需要注意的是,slotProps
为一个对象,获取的子组件也是一个对象,如下面 HTML 代码第 8-10 行注释所示。
如下代码所示:
Vue:
Vue.component('fun-div', {
// 接受父组件传来的值
props: ['list'],
// 下面模板中使用 <slot v-bind:msg='item'> 绑定了 msg 属性,将值传递到父组件中
template: `
<div>
<ul>
<li v-for="(item, index) in list" :key="item.id">
<slot v-bind:msg='item'>
{{item.name}}
</slot>
</li>
</ul>
</div>
`
})
let vm = new Vue({
el: '.container',
data: {
list: [
{id: 1, name: "apple"},
{id: 2, name: "banana"},
{id: 3, name: "orange"}]
}
})
HTML:
<div class="container">
<!-- 设置 list属性向子组件传值 -->
<fun-div :list='list'>
<!-- 通过 slot-scope 属性获得子组件中传递来的 数据
将数据存到了 slotProps 对象中
-->
<template slot-scope="slotProps">
<!-- 通过 slotProps 对象的 msg.name 访问每个数据的名称
因为 <slot v-bind:msg='item'> 绑定的是 msg 属性,所以需要.msg
-->
<strong v-if="slotProps.msg.id == 2">{{ slotProps.msg.name }}</strong>
<span v-else>{{ slotProps.msg.name }}</span>
</template>
</fun-div>
</div>
结果: