什么是 slot
<app>
<menu-main></menu-main>
<menu-sub></mneu-sub>
<div class="container">
<menu-left></menu-left>
<container></container>
</div>
<app-footer></app-footer>
</app>
当需要让组件组合使用,混合父组件的内容与子组件的模板时,就会用到slot,这个过程叫作内容分发(transclusion)。以<app>为例,它有两个特点:
•<app>组件不知道它的挂载点会有什么内容。挂载点的内容是由<app>的父组件决定的.
•<app>组件很可能有它自己的模板。
props传递数据、events触发事件和slot内容分发就构成了Vue组件的3个API来源,再复杂的组件也是由这3部分构成的。
作用域
比如父组件中有如下模板:
<child- component> {{message }} </child-component>
这里的 message 就是一个 slot,但是它绑定的是父组件的数据,而不是组件<child-component>的数据 。父组件模板的内容是在父组件作用域内编译,子组件模板的内容是在子组件作用域内编译。
<div id="app">
<child-component v-show="showChild"></child-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
//showChild 绑定父组件的数据
Vue.component('child-component',{
template:'<div>子组件</div>'
});
var app = new Vue({
el:'#app',
data:{
showChild:true
}
})
</script>
<div id="app">
<child-component></child-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
//showChild 绑定子组件的数据
Vue.component('child-component',{
template:'<div v-show="showChild">子组件</div>',
data:function(){
return {
showChild:true
}
}
});
var app = new Vue({
el:'#app'
})
</script>
slot 用法
单个 Slot
在子组件内使用特殊的<slot>元素就可以为这个子组件开启一个 slot (插槽),在父组件模板里,插入在子组件标签内的所有内容将替代子组件的<slot> 标签及它的内容。
<div id="app">
<child-component>
<p>分发的内容</p>
<p>更多分发的内容</p>
</child-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
//showChild 绑定子组件的数据
Vue.component('child-component',{
//子组件<slot> 内的备用内容,作用域是子组件本身.
template:'\
<div>\
<slot>\
<p>如果父组件没有插入内容,作为默认出现</p>\
</slot>\
</div>'
});
var app = new Vue({
el:'#app'
})
</script>
具名 Slot
给<slot>元素指定一个 name 后可以分发多个内容,具名 Slot 可以与单个 Slot 共存
<div id="app">
<child-component>
<h2 slot="header">标题</h2>
<p>正文内容</p>
<p>更多正文内容</p>
<div slot="footer">底部信息</div>
</child-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
Vue.component('child-component',{
template:'\
<div class="container">\
<div class="header">\
<slot name="header"></slot>\
</div>\
<div class="main">\
<slot></slot>\
</div>\
<div class="footer">\
<slot name="footer"></slot>\
</div>\
</div>'
});
var app = new Vue({
el:'#app'
})
</script>
子组件内声明了 3 个<slot>元素,其中在<div class="main">与内的<slot> 没有使用 name 特性,它将作为默认 slot 出现,父组件没有使用 slot 特性的元素与内容都将出现在这里。如果没有指定默认的匿名 slot,父组件内多余的内容片段都将被抛弃。
作用域插槽
作用域插槽是一种特殊的 slot,使用一个可以复用的模板替换己渲染元素。
<div id="app">
<child-component>
<template scope="props">
<p>来自父组件的内容</p>
<p>{{props.msg}}</p>
</template>
</child-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
Vue.component('child-component',{
template:'\
<div class="container">\
<slot msg="来自子组件的内容"></slot>\
</div>'
});
var app = new Vue({
el:'#app'
})
</script>
观察子组件的模板,在<slot>元素上有一个类似props传递数据给组件的写法msg="xxx",将数据传到了插槽。父组件中使用了<template>元素,而且拥有一个scope="props"的特性,这里的props只是一个临时变量,就像v-for="item in item"里面的item一样。template内可以通过临时变量props访问来自子组件插槽的数据msg。
作用域插槽更具代表性的用例是列表组件,允许组件自定义应该如何渲染列表每一项。
<div id="app">
<my-list :books="books">
<!--作用域插槽也可以是具名的Slot-->
<template slot="book" scope="props">
<li>{{props.bookName}}</li>
</template>
</my-list>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
/*子组件my-list接收一个来自父级的prop数组books,并且将它在name为book的slot
上使用v-for指令循环,同时暴露一个变量bookName。*/
Vue.component('my-list',{
props:{
books:{
type:Array,
default:function(){
return [];
}
}
},
template:'\
<ul>\
<slot name="book"\
v-for="book in books"\
:book-name="book.name">\
<!--这里可以写默认slot内容-->\
</slot>\
</ul>'
});
var app = new Vue({
el:'#app',
data:{
books:[
{name:'《西游记》'},
{name:'《水浒传》'},
{name:'《红楼梦》'},
{name:'《三国演义》'}
]
}
})
</script>
访问slot
<div id="app">
<child-component>
<h2 slot="header">标题</h2>
<p>正文内容</p>
<p>更多的内容</p>
<div slot="footer">底部信息</div>
</child-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
Vue.component('child-component',{
template:'\
<div class="container">\
<div class="header">\
<slot name="header"></slot>\
</div>\
<div class="main">\
<slot></slot>\
</div>\
<div class="footer">\
<slot name="footer"></slot>\
</div>',
mmounted:function(){
var header = this.$slots.header;
var main = this.$slots.default;
var footer = this.$slots.footer;
console.log(footer);
console.log(footer[0].elm.innerHTML);
}
});
var app = new Vue({
el:'#app'
})
</script>