当需要让组件组合使用,混合父组件的内容与子组件的模板时,就会用到slot,这个过程叫做内容分发。
1、slot内容分发
在子组件内使用特殊的<slot>元素就可以为这个子组件开启一个slot(插槽),在父组件模板里,插入在子组件标签内的所有内容将替代子组件的<slot>标签及它的内容。例如:
<div id="app">
<child-component>
<div>分发的内容</div>
<p>更多分发的内容</p>
</child-component>
<!-- 子组件里没有内容时,显示slot里面的内容 -->
<child-component></child-component>
</div>
<script>
/* slot 当子组件没有内容的时候显示(备用内容) */
Vue.component('child-component',{
template: '<div>\
<slot>如果父组件里的子组件标签里没有插入内容,我将作为默认出现</slot>\
</div>',
data: function(){
return {
name: '子组件的数据;'
}
}
});
var app = new Vue({
el: '#app'
});
</script>
没有name属性的slot将作为默认slot出现,父组件没有使用slot特性的元素与内容都将出现在这里;如果没有指定默认的匿名slot,父组件内多余的内容片段都将被抛弃。例如:
<div id="app">
<child-component>
<h2 slot="header">标题</h2>
<p>正文内容</p>
<p>更多的正文内容</p>
<div slot="footer">底部信息</div>
</child-component>
</div>
<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>
以上代码中,类名为main的div里面的slot没有name属性,是默认的slot,显示的内容正是子组件child-component里的两个没有slot属性的p标签里面的内容;子组件<slot>内的备用内容,它的作用域是子组件本身。
2、作用域插槽
作用域插槽是一种特殊的slot,使用一个可以复用的模板替换已渲染元素。例如:
<div id="app">
<child-component>
<!-- 临时变量props,用于访问来自子组件插槽的数据msg -->
<template slot-scope="props">
<p>来自父组件的内容</p>
<p>{{ props.msg }}</p>
</template>
</child-component>
</div>
<script>
/*
没有name的slot将作为默认slot出现,父组件没有使用slot特性的元素与内容都将
出现在这里;如果没有指定默认的匿名slot,父组件内多余的内容片段都将被抛弃
*
* */
Vue.component('child-component',{
template: `
<div class="container">
<slot msg="来自子组件的内容"></slot>
</div>
`
});
var app = new Vue({
el: '#app'
});
</script>
作用域插槽更具代表性的用例是列表组件;允许组件自定义应该如何渲染列表每一项。例如:
<div id="app">
<my-list :books="books">
<!-- 作用域插槽也可以是具名的slot -->
<template slot="book" slot-scope="props">
<li>{{ props.bookName }}</li>
</template>
</my-list>
</div>
<script>
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>
</ul>
`
});
var app = new Vue({
el: '#app',
data: {
books: [
{name: '《vue.js实战》'},
{name: '《javascript语言精粹》'},
{name: '《javascript高级程序设计》'}
]
}
});
</script>
3、访问slot
通过$slot可以访问某个具名slot,this.$slots.default包括了所有没有被包含在具名slot中的节点。例如:
<div id="app">
<child-component>
<h2 slot="header">标题</h2>
<p>正文内容</p>
<p>更多正文内容</p>
<div slot="footer">底部信息</div>
</child-component>
</div>
<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>
`,
mounted: function () {
var header = this.$slots.header;
var main = this.$slots.default;
var footer = this.$slots.footer;
console.log(footer); // 打印出footer元素的虚拟dom对象
console.log(footer[0].elm.innerHTML); // "底部信息"
}
});
var app = new Vue({
el: '#app'
});
</script>