在 Vue3 中,slot(插槽)是组件间传递内容的核心方式,template #名称 是 template v-slot:名称 的语法糖,用于绑定具名插槽。下面我会从基础到进阶,详细讲解用法并给出可直接运行的示例。

一、核心概念先理清
- 插槽的作用:让组件的部分内容由使用组件的父组件自定义,实现组件内容的灵活复用。
#语法糖 :#xxx等价于v-slot:xxx,只能用在<template>标签上(或组件标签本身,仅限默认插槽)。- 分类 :
- 具名插槽:有明确名称的插槽(如
#header、#content),用于组件内多位置自定义内容。 - 默认插槽:无名称的插槽(
#default,可省略),组件内未命名的插槽位置。 - 作用域插槽:插槽可以接收子组件传递的数据(核心:子传父,父组件自定义渲染逻辑)。
- 具名插槽:有明确名称的插槽(如
二、具体用法 + 完整示例
1. 基础:具名插槽(template #名称 核心用法)
步骤1:定义带具名插槽的子组件(MySlot.vue)
子组件通过 <slot name="xxx"> 定义插槽位置,name 对应父组件的 #xxx。
vue
<!-- MySlot.vue -->
<template>
<div class="slot-container">
<!-- 头部插槽 -->
<div class="header">
<slot name="header">默认头部内容(父组件没传则显示这个)</slot>
</div>
<!-- 内容插槽 -->
<div class="content">
<slot name="content">默认内容</slot>
</div>
<!-- 底部插槽(默认插槽,省略name) -->
<div class="footer">
<slot>默认底部内容</slot>
</div>
</div>
</template>
<style scoped>
.slot-container {
border: 1px solid #ccc;
padding: 20px;
width: 400px;
margin: 20px auto;
}
.header { border-bottom: 1px solid #eee; padding-bottom: 10px; }
.content { padding: 10px 0; }
.footer { border-top: 1px solid #eee; padding-top: 10px; color: #666; }
</style>
步骤2:父组件使用 template #名称 传递内容
父组件通过 <template #插槽名> 给对应插槽传递自定义内容:
vue
<!-- 父组件 App.vue -->
<template>
<div>
<h2>具名插槽示例</h2>
<MySlot>
<!-- 绑定header插槽:#header 等价于 v-slot:header -->
<template #header>
<h3>自定义的头部标题</h3>
</template>
<!-- 绑定content插槽 -->
<template #content>
<p>这是自定义的内容区域,可放任意HTML/组件</p>
<button>自定义按钮</button>
</template>
<!-- 默认插槽:#default 可省略,直接写内容 -->
<template #default>
<p>自定义的底部内容</p>
</template>
<!-- 简化写法:默认插槽无需template,直接写内容 -->
<!-- <p>自定义的底部内容</p> -->
</MySlot>
</div>
</template>
<script setup>
import MySlot from './components/MySlot.vue';
</script>
效果:
- 头部显示「自定义的头部标题」(替换了子组件的默认头部);
- 内容区显示自定义的文字+按钮;
- 底部显示「自定义的底部内容」。
2. 进阶:作用域插槽(带参数的 template #名称)
作用域插槽的核心是子组件向插槽传递数据 ,父组件通过 #名称="参数" 接收,再自定义渲染逻辑。
步骤1:子组件定义带数据的插槽(MyScopeSlot.vue)
通过 slot 标签的属性传递数据(如 :list="list"):
vue
<!-- MyScopeSlot.vue -->
<template>
<div class="scope-slot">
<h4>子组件的列表数据</h4>
<!-- 具名作用域插槽:向父组件传递list数据 -->
<slot name="list" :list="list" :title="'用户列表'">
<!-- 默认渲染逻辑(父组件没自定义则显示) -->
<ul>
<li v-for="item in list" :key="item.id">{{ item.name }}</li>
</ul>
</slot>
</div>
</template>
<script setup>
// 子组件的数据源
const list = [
{ id: 1, name: '张三', age: 20 },
{ id: 2, name: '李四', age: 22 },
{ id: 3, name: '王五', age: 24 },
];
</script>
步骤2:父组件接收并自定义渲染(App.vue)
通过 #list="slotProps" 接收子组件传递的参数(slotProps 是自定义名称,也可解构):
vue
<!-- 父组件 App.vue -->
<template>
<div>
<h2>作用域插槽示例</h2>
<MyScopeSlot>
<!-- 方式1:完整接收参数 -->
<!-- <template #list="slotProps">
<div>
<h5>{{ slotProps.title }}</h5>
<table border="1">
<tr v-for="item in slotProps.list" :key="item.id">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.age }}</td>
</tr>
</table>
</div>
</template> -->
<!-- 方式2:解构参数(推荐,更简洁) -->
<template #list="{ list, title }">
<div>
<h5>{{ title }}</h5>
<table border="1" cellpadding="5">
<tr>
<th>ID</th>
<th>姓名</th>
<th>年龄</th>
</tr>
<tr v-for="item in list" :key="item.id">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.age }}</td>
</tr>
</table>
</div>
</template>
</MyScopeSlot>
</div>
</template>
<script setup>
import MyScopeSlot from './components/MyScopeSlot.vue';
</script>
效果 :
父组件接收到子组件的 list 和 title,并将原本的列表渲染成了表格(替换了子组件的默认列表渲染)。
3. 特殊场景:默认插槽的语法糖简化
如果只有默认插槽,可直接在组件标签上用 #default(或省略),甚至直接绑定参数:
vue
<!-- 简化写法:默认插槽 -->
<MyScopeSlot #default="{ list }">
<ul>
<li v-for="item in list" :key="item.id">{{ item.name }} - {{ item.age }}岁</li>
</ul>
</MyScopeSlot>
<!-- 更简化:直接写在组件标签上(仅限默认插槽) -->
<MyScopeSlot v-slot="{ list }">
<p>默认插槽简化写法:{{ list[0].name }}</p>
</MyScopeSlot>
三、注意事项
#是v-slot:的语法糖,只能用在<template>标签上(默认插槽可例外,写在组件标签上);- 具名插槽必须匹配子组件
slot的name属性,否则会渲染默认内容; - 作用域插槽的参数可以解构,也可以重命名(如
#list="{ list: userList }"); - Vue3 中
slot和slot-scope已废弃,统一使用v-slot(或#)。
总结
template #名称是template v-slot:名称的语法糖,用于绑定具名插槽,核心是父组件向子组件指定位置传递自定义内容;- 作用域插槽通过
#名称="参数"接收子组件传递的数据,实现「子传父+父自定义渲染」; - 默认插槽可省略
#default,直接写内容,作用域插槽的参数支持解构,简化代码。
通过插槽(尤其是 # 语法糖),你可以灵活定制组件的内容,是 Vue 组件封装中最常用的技巧之一。