Vue 插槽
什么是插槽
slot翻译为插槽,在生活中有很多地方都有插槽,电脑的USB插槽,插板当中的电源插槽。
插槽的目的是让我们原来的设备具有更多的拓展性。
比如电脑的USB,我们可以接入U盘、硬盘、手机、音响、键盘、鼠标等等。
组件的插槽
组件的插槽也是为了让我们封装的组件更加具有拓展性。
让使用者可以决定组件内部的一些内容到底展示什么。
插槽就是在组件中预留的一个空间。可以自行选择在组件实例中插入哪些内容。
例如存在如下需求:
1、在页面中引入一组件,创建十个实例。每个实例都由自己的“个性”,1组件中使用a标签,2使用button,3使用input。。。
2、移动网站中的导航栏,封装为一个导航栏,在导航栏中预留若干个位置,预留五个插槽,在插槽中放入具体的实现。
如何封装这类组件?
抽取共性、保留不同
最好的封装方式就是将共性抽取到组件中,将不同暴露为插槽。
一旦我们预留了插槽,就可以让使用者根据自己的需求。决定插槽中插入什么内容。
是搜索框、是文字、还是菜单,由调用者自己来决定。
slot插槽
在子组件中,预留一对
子组件:
<template>
<div name='chidren'>
<h1 v-text="title"></h1>
<p v-text="info"></p>
<input type="text" v-model="dparam" @input="valChange">
<button @click="giao">发射属性到父组件</button>
<button @click="btnClick">在子组件中获取父组件</button>
<slot></slot>
</div>
</template>
slot中也可以添加默认值。添加了默认值以后,即使没有在组件的内部添加新标签,也会默认加上默认值。
添加默认值:
<template>
<div name='chidren'>
<h1 v-text="title"></h1>
<p v-text="info"></p>
<input type="text" v-model="dparam" @input="valChange">
<button @click="giao">发射属性到父组件</button>
<button @click="btnClick">在子组件中获取父组件</button>
<slot><button>我是默认值</button></slot>
</div>
</template>
如果添加了默认值,但是父组件内进行了填充,那么默认值就不会生效。
父组件:
<template>
<div name="parent">
<h1 v-text="title"></h1>
<input type="text" v-model="title">
<button @click="btnClick">访问子组件中的方法</button>
<Childred ref="child" @giao="giao" @title-change="titleChange" :title="title" :info="info">
<h2>插槽中的具体内容</h2>
</Childred>
</div>
</template>
如果在一个插槽中,添加多个标签,那么这几个标签都会显示。
具名插槽<2.6+已废除>
如果在一个组件内,需要预留多个插槽,但是预留多个插槽在父组件内添加元素时,默认会将三个插槽的都替换。这就需要预留插槽的名称,在父组件内创建子组件实例的时候。指定其名称。直接指定slot的name属性即可。在替换时,需要指定slot="name"
parent:
<template>
<div name='chidren'>
<h1 v-text="title"></h1>
<p v-text="info"></p>
<input type="text" v-model="dparam" @input="valChange">
<button @click="giao">发射属性到父组件</button>
<button @click="btnClick">在子组件中获取父组件</button>
<slot name="aa"><button>我是默认值</button></slot>
<slot name="bb"><button>我是默认值</button></slot>
<slot name="cc"><button>我是默认值</button></slot>
</div>
</template>
childred:
<template>
<div name="parent">
<h1 v-text="title"></h1>
<input type="text" v-model="title">
<button @click="btnClick">访问子组件中的方法</button>
<Childred ref="child" @giao="giao" @title-change="titleChange" :title="title" :info="info">
<h2 slot="bb">插槽中的具体内容</h2>
</Childred>
</div>
</template>
具名插槽2.6+ 版本推荐写法
子组件的定义没有变化,父组件的引入修改为了以下的形式:
//组件调用时
<MyFooter v-red :age.sync="age">
<template v-slot:footer>
//这里v-slot:后边的值与组件内的slot的name属性对应,也就是插槽的名称。
<div>list</div>
</template>
</MyFooter>
//书写组件时
<template>
<div>
{{age}}
<div>
<slot name='footer' />
//这里name的值就是这个插槽的名称。
</div>
</div>
</template>
即,在组件内部创建一个template标签,使用v-slot:name,代替原来的slot="name"
作用域插槽
作用域的介绍
真正的学习作用域插槽之前,我们需要先了解一个概念:编译作用域
Vue官方的解释:父组件模板的所有数据都会在父级作用域内编译,子组件模板的所有东西都会在子组件作用域内编译。
我理解的作用域就是 当父组件中使用子组件实例时,子组件中定义的data和父组件中的data的生效范围。何时会使用组件内部的data,何时会使用父组件中的data。
子组件:
<template>
<div name='chidren'>
<h1 v-text="title"></h1>
<p v-text="info"></p>
<input type="text" v-model="dparam" @input="valChange">
<button @click="giao">发射属性到父组件</button>
<button @click="btnClick">在子组件中获取父组件</button>
<slot name="aa"><button>我是默认值aa</button></slot>
<slot name="bb"><button>我是默认值bb</button></slot>
<slot v-show="isShow" name="cc"><button>我是默认值cc</button></slot>
</div>
</template>
<script>
export default {
name: 'chidren',
props: {
title: String,
info: String
},
data(){
return{
param: 'GIAO',
dparam: this.title,
isShow: false
}
},
methods:{
showInfo: function(){
alert(this.param);
},
giao: function(){
this.$emit('giao',this.param);
},
valChange: function(event){
let newVal = event.target.value;
this.param = newVal;
this.$emit('title-change',newVal);
},
btnClick: function(){
console.log(this.$parent.title);
}
}/*,
watch:{
dparam(oldVal,newVal){
this.param = newVal;
this.$emit('title-change',newVal);
}
}*/
}
</script>
<style>
</style>
父组件:
<template>
<div name="parent">
<h1 v-text="title"></h1>
<input type="text" v-model="title">
<button @click="btnClick">访问子组件中的方法</button>
<Childred v-show="isShow" ref="child" @giao="giao" @title-change="titleChange" :title="title" :info="info">
<h2 slot="bb">插槽中的具体内容</h2>
</Childred>
</div>
</template>
<script>
import Childred from './Chidren';
export default {
name: 'parent',
data(){
return {
title: 'rayfoo',
info: '真帅',
isShow: true
}
},
methods:{
giao: function(param){
alert(param);
},
titleChange: function(newVal){
this.title = newVal;
},
btnClick: function(){
console.log(this.$children[0].param);
this.$children[0].showInfo();
console.log(this.$refs.child.param);
}
},
components:{
Childred
}
}
</script>
<style>
</style>
此时,子组件和父组件中都有isShow,但是它们的作用范围却是不一样的
2.6+中使用作用域插槽
作用域插槽是slot中一个比较难以理解的点,且官方文档说的又有点不清晰。
这里,我们用一句话对其做一个总结,然后我们在后续的案例中来体会:
- 父组件替换插槽的标签,但是内容由子组件来提供
需求:
- 子组件中包括一组数据,比如clan['js','py','go']
- 需要在多个界面中进行展示:
- 某些界面以水平方向展示
- 某些页面是以列表形式的
- 某些是直接展示一个数组
- 内容在子组件中,但是希望父组件告诉我们如何去展示,该怎么办呢?
- 使用slot的作用域插槽即可。
//组件调用
<ul>
<myli :title="val.title"
>
<template v-slot:footer="message">
<div>{{message.aa}}</div>
</template>
</myli>
</ul>
//书写组件时
<template>
<li>
<slot name='footer' :aa="title">
</slot>
</li>
</template>
此处的message是一个自定义的名称,其保存slot中的属性集合,通过message可以调用slot中的所有属性,简化了slot-scope的操作
一句话概括就是v-slot :后边是插槽名称,=后边是组件内部绑定作用域值的映射。
另一个具名插槽+作用域插槽的案例
子组件:
data(){
return{
hobby: ['抽烟','喝酒','烫头']
}
}
<slot :hobby="hobby" name="cc"><button>我是默认值cc</button></slot>
父组件:
<Childred >
<template v-slot:cc="prop">
<ul v-for="(item,index) in prop.hobby" :key=index>
<li v-text="item"></li>
</ul>
</template>
</Childred>