上一章讲了组件将JS中的状态值传其它组件,但是如果是需要传模板内容(HTML代码)传给子组件来达到灵活使用组件的功能, 要实现这一功能就不得不说一下插槽了。
基本使用
使用方法:
- 父组件直接在子组件标签写模板内容,类似在HTML中嵌套HTML代码一样。
- 在子组件使用输出。
如:
子组件:
xml
<script setup>
const props = defineProps({
v: String
})
</script>
<template>
<div class="form-input-box">
<div class="form-input-lable">
<slot></slot> <!-- 在引输入父组件的插槽内容 -->
</div>
<div class="form-input">
<input type="text" v-model="props.v">
</div>
</div>
</template>
<style scoped>
.form-input-box {
margin: 20px 0 0;
width: 500px;
background-color: aquamarine;
}
.form-input-lable {
display: flex;
justify-content: space-between;
}
input {
width: 100%;
}
</style>
父组件:
xml
<script setup>
import { reactive } from "vue";
import MyInput from "@/components/MyInput.vue";
// 声明响应式状态数据
const data = reactive({
name: "",
xx: "",
love: ""
})
</script>
<template>
<!-- input1 -->
<MyInput :v="data.name"> <!-- 组件标签内部嵌套的部分就是插槽内容 -->
<label>姓名</label>
</MyInput>
<!-- input2 -->
<MyInput :v="data.xx"> <!-- 组件标签内部嵌套的部分就是插槽内容 -->
<label>血型</label>
<span>A、B、O、AB</span>
</MyInput>
</template>
页面上生成的HTML结构就为:
javascript
<div data-v-f33d1cc8="" class="form-input-box">
<div data-v-f33d1cc8="" class="form-input-lable">
<label>姓名</label> <!-- 插槽的内容原样输出在这里 -->
</div>
<div data-v-f33d1cc8="" class="form-input">
<input data-v-f33d1cc8="" type="text">
</div>
</div>
<div data-v-f33d1cc8="" class="form-input-box">
<div data-v-f33d1cc8="" class="form-input-lable"><!-- 组件标签内部嵌套的部分就是插槽内容 -->
<label>血型</label>
<span>A、B、O、AB</span> <!-- 插槽的内容原样输出在这里 -->
</div>
<div data-v-f33d1cc8="" class="form-input">
<input data-v-f33d1cc8="" type="text">
</div>
</div>
插槽默认内容
在外部没有提供任何内容的情况下,可以为插槽指定默认内容。比如:如果我们想在父组件没有提供任何插槽内容时在 <button>
内渲染"Submit",只需要将"Submit"写在 <slot>
标签之间来作为默认内容:
xml
<button type="submit">
<slot> Submit <!-- 默认内容 --> </slot>
</button>
具名插槽
一个组件的插槽是可以有多个的,不过需要为每个插槽命名,才知道每一次要渲染的内容。这种带命名的插槽就是具名插槽,像前面的那种没有命名的就是默认插槽,只不过默认插槽会有一个隐式的名称default
;
使用方法:
- 在父组件使用
template
包含插槽内容,并在template添加v-slot:插槽名
指令为其命名, 同样使用可用简写#插槽名
- 子组件的
slot
中使用name
属性来渲染对就插槽的内容。
父组件:
xml
<script setup>
import Container from "@/components/Container.vue";
</script>
<template>
<Container>
<template v-slot:header> <!-- 使用v-slot具名为 header -->
<div class="header">这是头部</div>
</template>
<template #default> <!-- 这里为默认插槽 -->
<div class="main">这是中间部分,使用的是默认插槽</div>
</template>
<template #footer> <!-- 使用简写为其具名 -->
<div class="footer">这是底部</div>
</template>
</Container>
</template>
子组件:
xml
<template>
<section>
<slot name="header"></slot> <!-- 渲染header插槽 -->
<slot></slot> <!-- 渲染默认插槽 -->
<slot name="footer"></slot> <!-- 渲染footer插槽 -->
</section>
</template>
作用域插槽(在插槽中传值)
一般情况下插槽的内容只能使用父组件中的状态数据,因为插槽的内容都是在父组件中定义的,使用状态数据也只能使用父组件定义的,但是我们可以像对组件传递props那样,向一个插槽的出口上传递属性值:
子组件中:
xml
<script setup>
import { ref } from "vue";
const msg = ref("插槽学习");
</script>
<template>
<section>
<slot :msg="msg"></slot> <!-- 将msg传给插槽 -->
</section>
</template>
父组件中
xml
<script setup>
import Container from "@/components/Container.vue";
</script>
<template>
<Container v-slot="slotProps"> <!-- 自定义一个名称接收插槽传来的属性值 -->
<p>{{ slotProps.msg }}</p> <!-- 使用属性msg -->
</Container>
</template>
条件插槽
有时你需要根据插槽是否存在来渲染某些内容时,可以结合使用 $slot
属性与v-if
来实现。
父组件:
xml
<script setup>
import Container from "@/components/Container.vue";
</script>
<template>
<Container>
<template #header>
<div class="header">header</div>
</template>
<template #default>
<div class="main">main</div>
</template>
<template #footer>
<div class="footer">footer</div>
</template>
</Container>
</template>
子组件:
xml
<template>
<div class="conainter">
<slot v-if="$slots.header" name="header"></slot> <!--如果存在header插槽就渲染-->
<slot v-if="$slots.default" name="default"></slot> <!--如果存在默认插槽就渲染-->
<slot v-if="$slots.footer" name="footer"></slot> <!--如果存在footer插槽就渲染-->
</div>
</template>
动态插槽
动态插槽就是动态的命名具名插槽,在不同的情况下使用不同命名的插槽。
结合上的条件插槽,以下面这个例子来介绍什么是动态插槽:
父组件:
xml
<script setup>
import { ref } from "vue";
import Container from "@/components/Container.vue";
const slotListNames = ['header', 'default', 'footer'];
const curSlotName = ref(slotListNames[0]);
//获取数组索引内的随机数
const getRandomIndex = (arr) => parseInt(Math.random() * slotListNames.length);
// 更新插槽名
const handleChang = () => {
curSlotName.value = slotListNames[getRandomIndex()];
}
</script>
<template>
<Container>
<template #[curSlotName]>
<div v-if="curSlotName === 'default' ">
这是默认插槽的内容
</div>
<div v-if="curSlotName === 'header'">
这是header插槽的内容
</div>
<div v-if="curSlotName === 'footer'">
这是footer插槽的内容
</div>
</template>
</Container>
<button @click="handleChang">更新插槽名</button>
</template>
子组件:
xml
<template>
<div class="conainter">
<slot v-if="$slots.header" name="header"></slot> <!--如果存在header插槽就渲染-->
<slot v-if="$slots.default" name="default"></slot> <!--如果存在默认插槽就渲染-->
<slot v-if="$slots.footer" name="footer"></slot> <!--如果存在footer插槽就渲染-->
</div>
</template>
到此Vue快速入门的基础知识也讲得差不多了,下一章节将会通过完成一个简单的任务管理器来综合的运用这些知识。