Vue 作用域插槽(Scoped Slots)学习笔记
一、核心概念
作用域插槽是 Vue 插槽的高级用法,核心解决了子组件数据向父组件插槽反向传递的问题:
- 普通插槽:仅支持父组件向子组件传递结构,无法访问子组件内部数据
- 作用域插槽:子组件通过"插槽 Prop"暴露内部数据,父组件接收后可自定义渲染逻辑,实现"逻辑复用+样式自定义"的灵活组合
本质上是子组件向父组件插槽传参,使插槽内容能与子组件数据联动。
二、核心差异:普通插槽 vs 作用域插槽
| 特性 | 普通插槽 | 作用域插槽 |
|---|---|---|
| 数据流向 | 父组件→子组件 | 子组件→父组件(插槽内) |
| 父组件访问子组件数据 | 不可 | 可(通过插槽 Prop 接收) |
| 典型场景 | 固定结构传递 | 动态结构渲染 |
三、基础实现步骤
1. 子组件:定义插槽并暴露数据
使用v-bind:属性名="数据"(简写:属性名="数据")为插槽绑定"插槽 Prop":
vue
<!-- 子组件 List.vue -->
<template>
<div class="list-container">
<div v-for="(item, index) in list" :key="item.id">
<!-- 暴露列表项、索引和激活状态 -->
<slot :item="item" :index="index" :isActive="index === currentIndex"></slot>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const list = ref([
{ id: 1, name: 'Vue', desc: '前端框架' },
{ id: 2, name: 'React', desc: 'UI库' },
{ id: 3, name: 'Angular', desc: '全栈框架' }
])
const currentIndex = ref(0)
</script>
2. 父组件:接收插槽 Prop 并渲染
使用v-slot:插槽名="接收变量"(简写#插槽名="接收变量")接收数据:
vue
<!-- 父组件 Parent.vue -->
<template>
<div>
<h3>框架列表</h3>
<List>
<!-- 完整写法 -->
<template v-slot:default="slotProps">
<div class="list-item" :class="{ active: slotProps.isActive }">
<span>{{ slotProps.index + 1 }}. {{ slotProps.item.name }}</span>
<p>{{ slotProps.item.desc }}</p>
</div>
</template>
</List>
</div>
</template>
3. 简化写法:解构插槽 Prop
vue
<List>
<!-- 解构写法 -->
<template #default="{ item, index, isActive }">
<div class="list-item" :class="{ active: isActive }">
<span>{{ index + 1 }}. {{ item.name }}</span>
<p>{{ item.desc }}</p>
</div>
</template>
</List>
四、进阶用法:具名作用域插槽
1. 子组件定义多个插槽
vue
<!-- 子组件 Card.vue -->
<template>
<div class="card">
<slot name="title" :title="cardTitle"></slot>
<slot name="content" :content="cardContent" :author="author"></slot>
<slot name="footer" :isLike="isLike" @toggleLike="toggleLike"></slot>
</div>
</template>
2. 父组件针对性接收数据
vue
<Card>
<template #title="{ title }">
<h2 class="card-title">{{ title }}</h2>
</template>
<template #content="{ content, author }">
<p class="card-content">{{ content }}</p>
<p class="card-author">作者:{{ author }}</p>
</template>
<template #footer="{ isLike, toggleLike }">
<button @click="toggleLike">
{{ isLike ? '已点赞' : '点赞' }}
</button>
</template>
</Card>
五、实用技巧
1. 默认插槽简写
vue
<!-- 简写前 -->
<List>
<template #default="{ item }">{{ item.name }}</template>
</List>
<!-- 简写后 -->
<List #="{ item }">
<span>{{ item.name }}</span>
</List>
2. 插槽 Prop 重命名
vue
<List #="{ item: frameworkItem }">
<span>{{ frameworkItem.name }}</span>
</List>
3. 设置默认值
vue
<List #="{ item = { name: '默认框架', desc: '无描述' } }">
<span>{{ item.name }}</span>
<p>{{ item.desc }}</p>
</List>
六、典型应用场景
- 列表组件:子组件处理数据获取和分页,父组件自定义项样式
- 表格组件:子组件管理表格结构,父组件定制单元格内容
- 容器组件:子组件提供基础结构和状态,父组件定义内容
- 通用组件:适应多种场景的可复用组件
七、注意事项
- 插槽 Prop 仅在该插槽模板内有效
- 避免过度使用,简单场景优先考虑普通插槽