概述
插槽是一种将父组件中的内容传递到子组件中的机制,可以让子组件定义一些可变的内容,从而增强组建的灵活性和复用性。
正常情况下,组件的开闭合标签中的内容是无法直接渲染出来的。例如有一个 Child
组件,我们直接在它的开闭合标签中添加一些内容:
<Child>
<p>Hello, world!</p>
</Child>
会发现 Hello, world!
在无法在页面中正常渲染,这时候就需要使用插槽 <slot></slot>
。
子组件:
<template>
<div class="child-container">
<!-- 放置插槽 -->
<slot></slot>
</div>
</template>
父组件:
<template>
<Child>
<p>Hello, world!</p>
</Child>
</template>
<script>
import Child from "@/components/Child.vue";
export default {
components: {
Child,
},
};
</script>
在页面渲染的时候就会将父组件中的 <p>Hello, world!</p>
顶替掉 子组件中 <slot></slot>
的位置。
注意:
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
(在使用插槽的时候,被插入的内容是无法访问到组件内部实例的方法或属性的。除了
作用域插槽
。)
插槽
默认插槽
默认插槽是没有名字的插槽,也称为 匿名插槽
。
默认插槽是最常用的一种插槽类型,将父组件中的内容会被自动插入到子组件的默认插槽中。
父组件:
<template>
<Child> Hello, world! </Child>
</template>
<script>
import Child from "@/components/Child.vue";
export default {
components: {
Child,
},
};
</script>
子组件:
<template>
<div class="child-container">
<p>
<!-- 默认插槽 -->
<slot></slot>
</p>
</div>
</template>
Hello, world!
会被自动被分配到子组件的 <slot></slot>
。
具名插槽
具名插槽就是有名字的插槽,它的作用的将父组件中的内容插入到子组件中指定的插槽位置。
子组件:
- 给
<slot></slot>
添加一个name
属性来设置名称。
<template>
<div class="child-container">
<p>
<!-- 具名插槽 -->
<slot name="text"></slot>
</p>
</div>
</template>
父组件:
- 使用
v-slot
指令,例如:v-slot:text
,指定内容插入到名为text
的<slot></slot>
中。
<template>
<Child>
<!--指定插入到名字为 text 的插槽中-->
<template v-slot:text>
Hello, world!
</template>
</Child>
</template>
<script>
import Child from "@/components/Child.vue";
export default {
components: {
Child,
},
};
</script>
v-slot
指令还可以简写成#
,例如:v-slot:text
可以写成#text
。
<Child>
<template #text>
Hello, world!
</template>
</Child>
作用域插槽
作用域插槽就是让插槽内容可以访问到子组件中的数据。
子组件:
<template>
<div class="child-container">
<p>
<!-- <slot> 标签中除了 name 属性以外,其他挂载的属性都会作为数据并用一个对象包裹着传递出去 -->
<slot name="text" msg="Hello,World" :count="count"></slot>
</p>
</div>
</template>
<script>
export default {
data() {
return {
count: 0
};
}
};
</script>
- 在
<slot></slot>
中,除了name
属性以外,其他属性都会被合成一个对象传出去。
父组件:
<template>
<Child>
<!-- 访问作用域插槽 -->
<template v-slot:text="data">
<!-- 子组件传递的数据 -->
<p>文字:{{ data.msg }}</p>
<p>计数:{{ data.count }}</p>
</template>
</Child>
</template>
<script>
import Child from "@/components/Child.vue";
export default {
components: {
Child,
},
};
</script>
- 注意,在
v-slot
指令外层无法访问到作用域插槽的数据。例如:
<template>
<Child>
<!-- 访问作用域插槽 -->
<template v-slot:text="data">
<!-- 子组件传递的数据 -->
<p>文字:{{ data.msg }}</p>
<p>计数:{{ data.count }}</p>
</template>
<!-- 作用域外访问 -->
{{ data.msg }}
</Child>
</template>
- 在
<template></template>
外面的data.msg
指向的是当前组件实例的数据对象。
默认作用域插槽
默认插槽内容使用子组件的数据。
父组件:
<template>
<Child>
<!-- 访问作用域插槽 -->
<template v-slot="data">
<!-- 子组件传递的数据 -->
<p>文字:{{ data.msg }}</p>
<p>计数:{{ data.count }}</p>
</template>
</Child>
</template>
默认插槽其实会被隐式命名为 default
,也就是不指定插槽名称时会自动使用 default
命名。
所以也可以通过 v-slot:default
指向默认插槽。
<template>
<Child>
<!-- 访问作用域插槽 -->
<template v-slot:default="data">
<!-- 子组件传递的数据 -->
<p>文字:{{ data.msg }}</p>
<p>计数:{{ data.count }}</p>
</template>
</Child>
</template>
还可以通过 #
简写形式成 #default
。
<template>
<Child>
<!-- 访问作用域插槽 -->
<template #default="data">
<!-- 子组件传递的数据 -->
<p>文字:{{ data.msg }}</p>
<p>计数:{{ data.count }}</p>
</template>
</Child>
</template>
解构插槽 Prop
作用域插槽接收过来的数据支持解构语法。
<template>
<Child>
<!-- 访问作用域插槽 -->
<template #default="{ msg, count }">
<!-- 子组件传递的数据 -->
<p>文字:{{ msg }}</p>
<p>计数:{{ count }}</p>
</template>
</Child>
</template>
动态插槽
动态指令参数也可以用在 v-slot
指令上。
<template>
<Child>
<!-- 访问作用域插槽 -->
<template #[text]="data">
<!-- 子组件传递的数据 -->
<p>文字:{{ data.msg }}</p>
<p>计数:{{ data.count }}</p>
</template>
</Child>
</template>
<script>
import Child from "@/components/Child.vue";
export default {
components: {
Child,
},
data () {
return {
text: "text"
}
}
};
</script>
重复的插槽内容
一个组件定义了多个同名的插槽,那么在使用该组件时,每个同名的插槽都会渲染对应的内容。就有可能导致一份内容渲染多次。
子组件:
- 定义两个
<slot name="text"></slot>
。
<template>
<div class="child-container">
<p>
<slot name="text"></slot>
</p>
<p>
<slot name="text"></slot>
</p>
</div>
</template>
父组件:
<template>
<Child>
<template #text>
Hello,World!
</template>
</Child>
</template>
<script>
import Child from "@/components/Child.vue";
export default {
components: {
Child,
},
data () {
return {
text: "text"
}
}
};
</script>
- 只插入了一个
Hello,World!
,但是页面上会出现两个