在 Vue 3 中,slot(插槽)是一种强大的组件内容分发机制,它允许父组件向子组件传递内容,从而使组件的使用更加灵活。以下是关于 Vue 3 中 slot 的详细介绍
一、默认插槽
-
这是最基本的插槽形式。子组件中使用定义一个插槽,父组件可以将内容放置在子组件标签对之间来填充这个插槽。
子组件代码示例 childTest.vue<template> <div class="child_wrap"> <slot></slot> </div> </template>
父组件代码示例parentTest.vue
<template> <childTest>1111</childTest> </template> <script setup lang="ts"> import childTest from './childTest.vue'; </script>
父组件中的
标签内容就会被插入到子组件的默认插槽位置,即子组件
内的位置
二、具名插槽
- 当子组件需要定义多个插槽时,就可以使用具名插槽来区分这些插槽。
- 在子组件中,给标签添加name属性来指定插槽的名称。父组件则通过v-slot:slotName或者简写#slotName来指定内容应该放置到哪个具名插槽
子组件代码示例 childTest.vue
<template>
<div class="child_wrap">
<slot></slot>
<slot name="header"></slot>
<slot name="content"></slot>
<slot name="footer"></slot>
</div>
</template>
父组件代码 parentTest.vue
<template>
<childTest>
<p>1111</p>
<template v-slot:header>
<h3>This is the header</h3>
</template>
<template #content>
<p>This is the content section</p>
</template>
<template v-slot:footer>
<p>This is the footer</p>
</template>
</childTest>
</template>
父组件通过v-slot:header、#content(简写形式)和v-slot:footer将不同的内容分别放入子组件的header、content和footer具名插槽中
三、作用域插槽
- 作用域插槽允许子组件向父组件传递数据,父组件可以基于这些数据来渲染内容。
- 在子组件中,通过在标签上使用v-bind(或简写:)将数据传递给插槽。父组件在使用作用域插槽时,可以通过模板内的slotProps对象来访问子组件传递的数据。
子组件代码
<template>
<div class="child_wrap">
<!-- <slot></slot>
<slot name="header"></slot>
<slot name="content"></slot>
<slot name="footer"></slot> -->
<slot name="item" :item="itemData"></slot>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const itemData = ref({
name: 'John',
age: 30,
})
</script>
父组件
<template>
<childTest>
<!-- <p>1111</p> -->
<!-- <template v-slot:header>
<h3>This is the header</h3>
</template>
<template #content>
<p>This is the content section</p>
</template>
<template v-slot:footer>
<p>This is the footer</p>
</template> -->
<template v-slot:item="slotProps">
<div>
<p>Age: {{ slotProps.item.age }}</p>
<p>Name: {{ slotProps.item.name }}</p>
</div>
</template>
</childTest>
</template>
子组件通过将itemData数据传递给名为item的作用域插槽。父组件通过v-slot:item="slotProps"接收这个数据,并在模板中使用slotProps.item来访问和渲染这些数据
四、动态插槽
-
动态插槽允许你根据条件动态地选择要渲染的插槽。这可以通过 v-bind 动态绑定插槽名来实现
<template>
子组件 childTest.vue<slot></slot> <slot name="dynamicSlot"></slot></template>
父组件 childTest.vue
<template>
<childTest>
1111
<template v-slot:[dynamicSlotName]>
<p>这是动态插槽的内容</p>
</template>
</childTest>
</template>
<script setup lang="ts">
import childTest from './childTest.vue';
import { ref } from 'vue';
const dynamicSlotName = ref('dynamicSlot');
</script>
<style scoped></style>
父组件通过v-slot:[dynamicSlotName]、#[dynamicSlotName](简写形式)
五、条件插槽
-
使用 $slots 属性与 v-if 来实现。
-
我们定义了一个卡片组件,它拥有三个条件插槽:header、footer 和 default。 当 header、footer 或 default 的内容存在时,我们希望包装它以提供额外的样式
<template>
父组件 parentTest.vue<childTest> <template v-slot:header></template> <script setup lang="ts"> import childTest from './childTest.vue'; const handleClick = () => { console.log('按钮被点击了'); }; </script>卡片标题
</template><!-- 默认插槽(内容) --> <p>这是卡片的主体内容。你可以在这里添加任意内容。</p> <!-- 页脚插槽 --> <template v-slot:footer> <button @click="handleClick">操作按钮</button> </template> </childTest>
子组件 childTest.vue
<div class="card">
<div v-if="$slots.header" class="card-header">
<slot name="header" />
</div>
<div v-if="$slots.default" class="card-content">
<slot />
</div>
<div v-if="$slots.footer" class="card-footer">
<slot name="footer" />
</div>
</div>
</template>
在 Vue 3 中,你可以使用 v-slots 指令来以对象的形式定义插槽内容。这种方式允许你在模板中以更灵活的方式定义多个插槽。以下是一个示例,展示如何使用 v-slots 来定义默认插槽和其他插槽的内容。
父组件代码
<template>
<div>
<childTest
v-slots="{
default: () => <p>这是默认插槽的内容</p>,
header: () => <h3>这是标题插槽的内容</h3>,
footer: () => <button @click="handleClick">这是页脚插槽的内容</button>
}"
/>
</div>
</template>
<script setup lang="ts">
import childTest from './childTest.vue';
const handleClick = () => {
console.log('按钮被点击了');
};
</script>
<style scoped></style>
子组件代码(childTest.vue)
<template>
<div class="card">
<div v-if="$slots.header" class="card-header">
<slot name="header" />
</div>
<div v-if="$slots.default" class="card-content">
<slot />
</div>
<div v-if="$slots.footer" class="card-footer">
<slot name="footer" />
</div>
</div>
</template>
<style scoped>
.card {
border: 1px solid #e0e0e0;
border-radius: 8px;
overflow: hidden;
margin: 20px 0;
}
.card-header {
background-color: #f5f5f5;
padding: 16px;
border-bottom: 1px solid #e0e0e0;
}
.card-content {
padding: 16px;
}
.card-footer {
background-color: #f5f5f5;
padding: 12px 16px;
border-top: 1px solid #e0e0e0;
text-align: right;
}
</style>