定义
- 为了让组件可以组合,需要一种方式来混合父组件的内容与子组件自己的模板,这个过程称为内容分发。Vue.js实现了一个内容分发API,使用特殊的slot元素作为原始内容的插槽。
- 可以理解为slot就是个插槽,要分发的内容将要插到这个在子组件里预留好的插槽内(替换slot标签)。
- 插槽,也就是slot,是组件的一块HTML模板,这块模板显示不显示,以及怎样显示是由父组件来决定,最核心问题就是显不显示怎样显示。
- 插槽模板是slot,它是一个空壳子,因为它的显示隐藏以及最后用什么样的HTML模板显示由父组件控制的,但是插槽显示的位置却由子组件自身决定,slot写在组件template的什么位置,父组件传过来的模板就来就显示什么位置。
编译作用域
父组件模板的内容在父组件作用域内编译;
子组件模板的内容在子组件作用域内编译;
<child-tem>
{{ message }}
</child-tem>
message应该绑定到父组件的数据;
下面这个错误是将父组件的一个指令绑定到子组件的属性方法:
<!-- childProperty是子组件的属性,上例不会如期那样工作,父组件不应该知道子组件的状态 -->
<child-component v-show='childProperty'></child-component>
如果要绑定作用域内的指令到一个组件的根节点,你应当在自己的模板上做:
Vue.component('child-component',{
// 这才是正确的作用域内
template:'<div v-show='childProperty'>child</div>'
data:function(){
return{
childProperty:true
}
})
单个slot
子组件至少要有一个 ‘slot’插口,不然父组件的内容将会被丢弃(被slot标签的内容替换)。如果子组件模板只有一个没有属性的slot时,父组件整个内容片段将插入到‘slot’所在DOM位置。并替换掉slot标签本身。
最初在‘slot’标签中的任何内容都被视为备用内容。备用内容在子组件的作用域内编译,并且只有在宿主元素为空,且没有要插入内容是才显示备用内容。
<div id='app'>
<h1>我是父组件的标题</h1>
<my-component>
<p>初始内容</p>
<p>初始内容</p>
</my-component>
</div>
Vue.component('my-component',{
template:'
<div>
<h2>我是子组件标题</h2>
<slot>只有在没有要分发的内容时才显示</slot>
</div>',
})
new Vue({
el:'#app'
})

子组件slot的位置就是要分发的‘初始内容的2个p标签’
<div id='app'>
<h1>我是父组件的标题</h1>
<!-- 测试这个组件里没有要奋发的内容 -->
<my-component><my-component>
</div>

具名slot
slot元素可以用一个特殊的属性name来配置如何分发内容。多个slot可以有不同名字,具名slot将匹配内容片段中对应slot特性元素
也可以有一个匿名slot,默认是slot,作为找不到匹配的内容片段的备用插槽。如果没有默认的slot,这些找不到匹配的内容片段将会被抛弃。
<div id='app'>
<my-component>
<h1 slot='header'>这是标题</h1>
<p>第一个段落</p>
<p>第二个段落</p>
<p slot='footer'>联系信息</p>
</my-component>
</div>
Vue.component('my-component',{
template:'
<div class='container'>
<header>
<slot name='header'></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name='footer'></slot>
</footer>
</div>',
})
new Vue({
el:'#app'
})

作用域插槽
作用域是一种特殊类型的插槽,使用一个可重用模板替换已渲染元素。在子组件中,只需将数据传递到插槽,就像你将props传递给组件一样
也称作是带数据的插槽
<div id='app'>
<my-component>
<template scope='props'>
<p>hello form parent</p>
<p>{{ prosp.text }}</p>
</template>
</my-component>
</div>
Vue.commponent('my-component',{
template:'
<div class='child'>
<slot text='hello'></slot>
</div>
',
props:['text']
})
new Vue({
el:'#app'
})

在父级中,具有特殊属性的scope的<template>元素必须存在,表示它是作用域插槽的模板。scope的值对应一个临时变量名,此变量接收从子组件中传递的props对象。
作用域插槽实现列表组件
<div id="app">
<h1>我是父组件的标题</h1>
<my-component :items="items">
<template scope="props" slot="item">
<li>{{ props.text }}</li>
</template>
</my-component>
</div>
Vue.component('my-component',{
template:'<ul><slot name="item" v-for="item in items" :text="item.text"></slot></ul>',
props:[ 'text','items']
});
new Vue({
el:'#app',
data:{
items:[
{text:'item1'},
{text:'item2'},
{text:'item3'}
]
}
})
****** 这编辑器真是太垃圾了 ******
深剖析作用域插槽
父组件
<div class="parent">
<child msg="msg">
<template scope="aaa">
<span>{{ aaa.text }}</span>
<span>{{ aaa.text2 }}</span>
</template>
</child>
</div>
子组件
<div class="child">
<slot text="hi" text2="hello"></slot>
</div>
- 这里的scope属性的值是一个变量名aaa,这个aaa指向一个对象{ text='hi',text2='hello'},这个对象正是子组件传递过来的<slot>标签属性的集合。因此我们可以通过<slot>标签属性绑定数据,父组件模板通过scope属性指定一个变量来接收。
- 那为什么在注册组件时还要加上props选项?上面例子父组件给组件传递了msg这个数据<child msg='msg'>,此时子组件必须在props选项中声明msg变量才能接收。