以下是 Vue 中普通插槽(Slot)与作用域插槽(Scoped Slot)的对比解析:
一、核心差异概览
特性 | 普通插槽 | 作用域插槽 |
---|---|---|
数据来源 | 父组件定义内容,使用父组件数据 | 子组件提供数据,父组件决定渲染方式 |
数据流向 | 父 → 子(单向传递内容) | 子 → 父(传递数据)→ 父渲染内容 |
作用域 | 内容在父作用域编译 | 内容可访问子组件数据 |
典型场景 | 通用内容填充(如布局容器) | 数据驱动自定义渲染(如列表模板) |
二、普通插槽(Default Slot)
1. 工作流程
2. 代码示例
子组件 (Child.vue)
<template>
<div class="card">
<slot>默认内容(父未传递时显示)</slot>
</div>
</template>
父组件 (Parent.vue)
<Child>
<!-- 父组件定义内容(可访问父数据) -->
<h2>{{ parentTitle }}</h2>
<p>来自父组件的内容</p>
</Child>
3. 特点
- 内容由父组件完全控制
- 只能使用父组件数据(无法访问子组件内部状态)
- 支持后备内容(父未提供时显示)
三、作用域插槽(Scoped Slot)
1. 工作流程
2. 代码示例
子组件 (List.vue)
<template>
<ul>
<li v-for="item in items" :key="item.id">
<!-- 向父组件暴露 item 数据 -->
<slot :item="item">{{ item.name }}(默认)</slot>
</li>
</ul>
</template>
<script>
export default {
data() {
return { items: [{id:1, name:'Apple'}, {id:2, name:'Banana'}] }
}
}
</script>
父组件 (Parent.vue)
<List>
<!-- 接收子组件数据并自定义模板 -->
<template v-slot:default="slotProps">
<span class="highlight">{{ slotProps.item.id }}. </span>
{{ slotProps.item.name.toUpperCase() }}
</template>
</List>
3. 特点
- 子组件通过
<slot>
暴露数据(如:item="item"
) - 父组件通过
v-slot
接收数据(可解构{item}
) - 父组件基于子组件数据动态定义渲染逻辑
- 支持命名插槽(
v-slot:name
)多区域分发
四、核心区别详解
-
数据控制权
- 普通插槽:父组件提供数据和内容
- 作用域插槽:子组件提供数据,父组件提供内容模板
-
作用域边界
- 普通插槽:内容在父作用域编译,无法访问子组件变量
- 作用域插槽:模板在父组件定义,但可访问子作用域数据
-
灵活性
作用域插槽允许父组件根据子组件数据动态生成内容,典型场景:<!-- 自定义表格单元格 --> <DataTable> <template v-slot:row="{ data }"> <td v-if="data.type === 'price'">{{ formatCurrency(data.value) }}</td> <td v-else>{{ data.value }}</td> </template> </DataTable>
五、使用建议
场景 | 推荐类型 |
---|---|
固定内容填充(如页头/页脚) | 普通插槽 |
子组件提供数据的动态渲染 | 作用域插槽 |
组件库开发(如表格、列表) | 作用域插槽 |
关键总结:作用域插槽通过 “子传数据,父定模板” 实现关注点分离,是构建高复用组件的核心模式。