插槽
插槽是组件内预留的槽口,通过这个槽口可以插入代码,但是组件必须使用双标签
插槽的分类
匿名插槽 <slot></slot>
具名插槽 <slot name='插槽名'></slot>
作用域插槽
<slot name='插槽名'></slot>
<template v-slot:'插槽名'=scope></template>或
<template #:'插槽名'=scope></template>或
<template slot='插槽名' slot-scope='scope'></template>
匿名插槽(默认插槽)
<first>
<template v-slot:default>
匿名插槽的数据
</template>
</first>
或<first>这里是匿名插槽的数据</first>
<slot></slot>
<div id="app">
<!-- 使用slot插槽必须使用双标签 -->
<first>这里是匿名插槽的数据</first>
</div>
<script src="https://unpkg.com/vue@2.7.14/dist/vue.js"></script>
<script>
let first = {
//slot不能作为对外暴露标签
template: `
<div>
匿名插槽:
<slot></slot>
</div>`
}
new Vue({
el: "#app",
data: {
},
components: {
first
}
})
</script>
具名插槽
- v-slot:插槽名(vue2.6版本及以上支持)简写:#插槽名
- slot="插槽名"
可以使用template标签包裹,也可以使用其他标签包裹,使用template包裹的插槽不会创建标签,而其他标签包裹的会创建对应的标签,建议使用template包裹因为更高效
<div id="app">
<first>
<!-- 可以使用v-slot:插槽名 -->
<template v-slot:tom>
template包裹《v-slot:插槽名绑定》的数据
</template>
<!-- 使用template不会创建标签更高效,建议使用template包裹-->
<template slot="jack">
template包裹《slot="插槽名"绑定》的数据
</template>
<!-- 还可以使用#插槽名 -->
<template #marray>
template包裹《#插槽名绑定》的数据
</template>
<!-- 使用div会创建div标签-->
<div slot="tonny">
div包裹tonny插槽的数据
</div>
<template v-slot:default>
template包裹匿名插槽的数据
</template>
</first>
</div>
<script src="https://unpkg.com/vue@2.7.14/dist/vue.js"></script>
<script>
let first = {
// 具名插槽通过name属性来设置
template: `
<div>
具名插槽:<slot name="tom"></slot><br>
具名插槽:<slot name="jack"></slot><br>
具名插槽:<slot name="marray"></slot><br>
具名插槽:<slot name="tonny"></slot><br>
默认插槽:<slot></slot>
</div>`
}
new Vue({
el: "#app",
data: { },
components: {
first
}
})
</script>
作用域插槽
- 扩大了对应变量的作用域,可以在对应的父组件内容使用子组件的数据。
- 作用域插槽本身一般是一个具名插槽。
- 该插槽的不同点是在子组件渲染作用域插槽时,可以将子组件内部的数据传递给父组件,让父组件根据子组件的传递过来的数据决定如何渲染该插槽。
方法一: <template slot="插槽名" slot-scope="scope">(vue2.6版本之前)
<div id="app">
<!-- 父组件调用 -->
<first>
<template slot="mySlot" slot-scope="scope">
<div>
{{scope.a}}<br>{{scope.b}}<br>
<button @click="scope.c.username='tonny'">改变user的username</button><br>
<button @click="scope.d()">调用方法</button>
<br>
<button @click="scope.f(message)">更改父组件数据</button>
</div>
</template>
</first>
</div>
<script src="https://unpkg.com/vue@2.7.14/dist/vue.js"></script>
<script>
let first = {
template: `
<div>{{user.username}}
<slot name="mySlot" :a="msg" b='你好' :c="user" :d="sayWhat" :f="tips"></slot>
</div>
`,
data() {
return {
msg: "吃饭了么",
user: {
username: 'tom'
}
}
},
methods: {
sayWhat() {
console.log("说啥呢");
},
tips(arg) {
console.log(arg);
}
},
}
new Vue({
el: "#app",
data: {
message: "父组件"
},
components: {
first
}
})
</script>
方法二:v-slot:插槽名=“接收的属性对象名”(一般为scope)(vue2.6版本及之后)
(v-slot:插槽名)可以缩写为(#:插槽名)
<div id="app">
<first>
<!-- v-slot:插槽名=“接收的属性对象名(一般为scope)” -->
<template v-slot:mySlot>
<div>
{{scope.message}}
<button @click="scope.a.username='tom'">更改user的username</button>
</div>
</template>
</first>
</div>
<script src="https://unpkg.com/vue@2.7.14/dist/vue.js"></script>
<script>
let first = {
template: `
<div>{{msg}}{{user.username}}
<slot name='mySlot' :message="msg" :a="user"></slot>
</div>`,
data() {
return {
msg: "子组件",
user: {
username: "tonny"
}
}
}
}
new Vue({
el: "#app",
data: {},
components: {
first
}
})
</script>
【总结】
- 使用slot来定义插槽
- 一个组件中只能有一个匿名插槽。
- 匿名插槽又称为默认插槽,内容默认传递给对应的具名插槽
- slot标签中通过name属性来指定对应的插槽名字
- 访问的时候通过slot属性来指定需要插入到的插槽名字
- 具名插槽建议使用template标签包起来调用
- 作用域插槽是扩大了对应的组件作用域,通过对应的插槽中数据的绑定来传递数据给对应的父组件
- v-slot不要和slot的写法混用(区分版本来使用,不要一起使用)
组件的获取
相关属性
- $root 获取根组件
- $children 获取所有的子组件
- $parent 获取父组件
- $refs 获取对应的ref标记的组件
获取插槽相关属性
- $slots 获取调用的所有插槽 (不包含作用域插槽)
- $scopedSlots 获取调用的作用域插槽
<div id="app">
<father>
<template slot="father">
<div>
</div>
</template>
</father>
</div>
<script src="https://unpkg.com/vue@2.7.14/dist/vue.js"></script>
<script>
let son = {
template: `
<div>{{msg}}
<button @click="handler">获取组件</button>
<button @click="handler1">获取插槽</button>
<slot></slot>
<slot name="son"></slot>
</div> `,
data() {
return {
msg: "儿子"
}
},
methods: {
handler() {
console.log(this.$parent); //获取的是father组件
console.log(this.$root); //获取的是vue实例的组件
},
handler1() {
console.log(this.$slots); //
console.log(this.$scopedSlots); //获取的是自己的具名插槽
}
},
}
let father = {
template: `
<div>{{msg}}
<son>
<template slot="son" slot-scope="scope">
<div>
{{scope.msg}}
</div>
</template>
</son>
<button @click="handler2">获取组件</button>
<button @click="handler3">获取插槽</button>
<slot></slot>
<slot name="father"></slot>
</div>`,
data() {
return {
msg: '父亲'
}
},
components: {
son
},
methods: {
handler2() {
console.log(this.$parent); //获取的是vue实例的组件
console.log(this.$root); //获取的是vue实例的组件
console.log(this.$children); //获取的是son组件
},
handler3() {
console.log(this.$slots); //获取插槽名为father的插槽,不包含scope作用域插槽
console.log(this.$scopedSlots); // 获取调用的作用域插槽
}
}
}
new Vue({
el: "#app",
data: {
msg: "爷爷"
},
components: {
father
}
})
</script>
【理解】props和slot比较
- props 用来传递数据,它不是响应式的,所以是单向数据传输
- slot 用来传递结构
- 作用域插槽用来传递结构+数据