Vue 插槽
在某些场景中,我们可能需要从父组件向子组件传递模板内容。例如,一个通用的卡片组件,我们希望它的主体内容是可定制的。这时,就需要使用 插槽 (Slots)。
插槽是 Vue 组件的一个强大功能,它允许你以一种预定义的方式组合组件,将父组件的内容“插入”到子组件的指定位置。
默认插槽 (Default Slot)
最简单的插槽形式是单个的、默认的插槽。子组件可以使用 <slot> 标签来为其自身开辟一个“内容出口”。
FancyButton.vue (子组件)
<template>
<button class="fancy-btn">
<slot></slot> <!-- 插槽出口 -->
</button>
</template>
<style>
.fancy-btn {
background: linear-gradient(315deg, #42d392 25%, #647eff);
border: none;
padding: 8px 16px;
border-radius: 8px;
color: white;
cursor: pointer;
}
</style>父组件在使用 FancyButton 时,可以向其内部传递任何模板内容。这些内容将被渲染在子组件的 <slot> 标签所在的位置。
App.vue (父组件)
<script setup>
import FancyButton from './FancyButton.vue'
</script>
<template>
<FancyButton>
Click me! <!-- 插槽内容 -->
</FancyButton>
</template>渲染出的 HTML 将是:
<button class="fancy-btn">
Click me!
</button>具名插槽 (Named Slots)
有时我们需要多个插槽。例如,一个带有页头、主体和页脚的布局组件。为此,<slot> 元素有一个特殊的 attribute name,可以用来给不同的插槽分配唯一的 ID。
BaseLayout.vue (子组件)
<template>
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot> <!-- 默认插槽 -->
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>要在父组件中向具名插槽提供内容,我们需要使用一个带有 v-slot 指令的 <template> 元素,并将插槽名作为 v-slot 的参数。
App.vue (父组件)
<script setup>
import BaseLayout from './BaseLayout.vue'
</script>
<template>
<BaseLayout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<template v-slot:default>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</BaseLayout>
</template>v-slot 有一个专门的简写语法 #,所以 <template v-slot:header> 可以被简写为 <template #header>。
作用域插槽 (Scoped Slots)
有时,让插槽内容能够访问子组件中才有的数据是很有用的。例如,一个列表组件,我们希望父组件能自定义每一项的渲染方式。
为了实现这一点,我们可以像对 prop 那样,给插槽传递 attribute。
MyComponent.vue (子组件)
<script setup>
import { ref } from 'vue'
const items = ref(['Feed a cat', 'Buy milk'])
</script>
<template>
<ul>
<li v-for="(item, index) in items">
<slot :item-text="item" :index="index"></slot>
</li>
</ul>
</template>现在在父级作用域中,我们可以使用带值的 v-slot 来定义我们提供的插槽 prop 的名字。
App.vue (父组件)
<script setup>
import MyComponent from './MyComponent.vue'
</script>
<template>
<MyComponent v-slot="slotProps">
<i>{{ slotProps.itemText }} (index: {{ slotProps.index }})</i>
</MyComponent>
</template>在这个例子中,我们选择将包含所有插槽 prop 的对象命名为 slotProps,但你也可以使用任意你喜欢的名字。你也可以使用解构赋值来获取特定的 prop:
<MyComponent v-slot="{ itemText, index }">
<i>{{ itemText }} (index: {{ index }})</i>
</MyComponent>作用域插槽使得我们可以创建出高度灵活和可复用的组件,将数据逻辑封装在子组件中,同时将视图表现的控制权交给父组件。