Slots(插槽)是 Vue 提供的内容分发机制,用于实现父组件向子组件传递自定义 HTML 结构、文本或组件,极大提升组件的灵活性和复用性
插槽的核心作用:子组件定义占位位置,父组件填充实际内容,完美解耦组件结构与业务内容,是组件化开发的必备技能。
默认插槽是最基础的插槽形式,无需命名,子组件用 <slot> 定义占位,父组件直接在组件标签内书写内容即可渲染。
<!-- 子组件 Child.vue -->
<template>
<div class="child-box">
<h3>子组件固定内容</h3>
<!-- 默认插槽 -->
<slot>默认占位内容(父组件不传值时显示)</slot>
</div>
</template>
<!--
父组件 Parent.vue
1. 在 App.vue 中 import 观察效果
2. 删除 <Child> 中的全部内容再观察效果
-->
<template>
<Child>
<!-- 内容自动插入默认插槽 -->
<p>这是父组件传入的自定义内容</p>
</Child>
</template>
<script setup lang="ts">
import Child from './Child.vue'
</script>当组件需要多个插槽位置时,使用具名插槽。子组件通过 name 属性定义插槽名称,父组件通过 #插槽名(v-slot 简写)指定内容插入位置。
<!-- 子组件 Child.vue -->
<template>
<div class="child-box">
<!-- 具名插槽:头部 -->
<header>
<slot name="header"></slot>
</header>
<!-- 默认插槽:主体 -->
<main>
<slot></slot>
</main>
<!-- 具名插槽:底部 -->
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
<!-- 父组件 Parent.vue -->
<template>
<Child>
<template #header>
<h4>自定义头部</h4>
</template>
<!-- 无模板标签 = 默认插槽 -->
<p>自定义主体内容</p>
<template #footer>
<span>自定义底部</span>
</template>
</Child>
</template>
<script setup lang="ts">
import Child from './Child.vue'
</script>作用域插槽可以让子组件向父组件传递数据,父组件能使用子组件内部的数据渲染插槽内容,是高阶插槽用法。
<!-- 子组件 List.vue -->
<template>
<ul>
<li v-for="item in list" :key="item.id">
<!-- 子组件通过 v-bind 向插槽传递数据 -->
<slot :row="item" :index="item.id"></slot>
</li>
</ul>
</template>
<script setup lang="ts">
import { ref } from 'vue'
// 子组件内部数据
const list = ref([
{ id: 1, name: 'Vue 3 插槽' },
{ id: 2, name: 'Vue 3 组件通信' }
])
</script>
<!-- 父组件 Parent.vue -->
<template>
<List>
<!-- 解构子组件传递的数据 -->
<template #default="{ row }">
<span>{{ row.name }}</span>
</template>
</List>
</template>
<script setup lang="ts">
import List from './List.vue'
</script>基于我们之前开发的 MyText 组件,保留 Props、自定义事件、双向绑定 功能,新增后缀具名插槽(suffix),实现输入框后置自定义内容(如 元、个、次 等单位)。
修改 src/components/MyText/src/index.vue
<template>
<div class="my-text-wrapper">
<label class="my-label" for="text-input">{{ label }}:</label>
<input
id="text-input"
type="text"
class="my-input-text"
:value="modelValue"
@input="handleInput"
:placeholder="placeholder"
:disabled="disabled"
/>
<!-- 具名插槽:suffix 后缀,支持父组件自定义内容 -->
<slot name="suffix" class="suffix-text">(请填写单位)</slot>
<button class="submit-btn" @click="handleSubmit">提交</button>
</div>
</template>
<script setup lang="ts">
// 编译时宏,无需手动导入
// 定义 Props
const props = defineProps({
label: { type: String, required: true },
placeholder: { type: String, default: '请输入内容' },
disabled: { type: Boolean, default: false },
modelValue: { type: String, default: '' }
})
// 定义自定义事件
const emit = defineEmits<{
'update:modelValue': [value: string]
'submit': [label: string, value: string]
}>()
// 输入框双向绑定
const handleInput = (e: Event) => {
const value = (e.target as HTMLInputElement).value
emit('update:modelValue', value)
}
// 提交事件
const handleSubmit = () => {
emit('submit', props.label, props.modelValue)
}
</script>
<style scoped>
.my-text-wrapper {
display: flex;
align-items: center;
gap: 8px;
margin: 12px 0;
}
.my-label {
font-size: 14px;
color: #333;
}
.my-input-text {
padding: 8px 12px;
border-radius: 4px;
border: 1px solid #ccc;
outline: none;
}
.suffix-text {
font-size: 14px;
color: #666;
}
.submit-btn {
padding: 8px 16px;
border-radius: 4px;
border: none;
background-color: #409eff;
color: #fff;
cursor: pointer;
}
</style>在父组件中,通过 #suffix 使用具名插槽,自定义输入框后置内容,保持组件功能连贯:
<template>
<div class="demo-box">
<h3>MyText 组件 - 插槽实战</h3>
<!-- 1. 不使用插槽:显示默认插槽内容 -->
<MyText label="商品数量" v-model="count" />
<!-- 2. 使用具名插槽 suffix:自定义后置内容 -->
<MyText label="支付金额" v-model="price" @submit="handleSubmit">
<template #suffix>
<span class="unit"> 元 </span>
</template>
</MyText>
</div>
</template>
<script setup lang="ts">
import { MyText } from './components/MyText'
import { ref } from 'vue'
// 响应式数据
const count = ref('')
const price = ref('')
// 监听提交事件
const handleSubmit = (label: string, value: string) => {
alert(`${label}:${value}`)
}
</script>
<style scoped>
.demo-box {
padding: 20px;
}
.unit {
color: #f56c6c;
font-weight: bold;
}
</style>默认插槽:子组件 <slot>,父组件直接书写内容
具名插槽:子组件 <slot name="xxx">,父组件 <template #xxx>
作用域插槽:子组件 <slot :数据名="值">,父组件解构使用 #default="{ 数据名 }"
插槽支持默认内容:父组件不传值时,显示子组件定义的默认文本
插槽可以传递任意内容:文本、HTML 标签、其他 Vue 组件
严格遵循 Vue 3 语法,# 是 v-slot 的官方简写,优先级最高
与 Props、自定义事件配合,构建高复用、高灵活的企业级组件
插槽是 Vue 内容分发的核心,分为默认、具名、作用域三种形式
具名插槽适合多区域分发内容,作用域插槽支持子向父传数据
实战中为 MyText 增加插槽后,组件适配更多业务场景,复用性大幅提升
Vue 3 中 # 简写语法是标准用法,简洁且易于维护