在 Vue 中,Props 和插槽(Slot)是组件间通信的两种核心方式,它们各自解决不同场景的问题,理解两者的区别和适用场景是掌握 Vue 组件设计的关键。
一、Props:父组件向子组件传递「数据」的桥梁
Props 是 Vue 中父组件向子组件传递数据的标准方式,本质是「数据的单向传递」(父组件数据变化会影响子组件,但子组件不能直接修改 props 数据)。
1. 核心作用
- 让父组件能向子组件传递配置信息、状态或业务数据(如标题、开关状态、列表数据等)。
- 子组件通过 props 接收数据后,按自身逻辑进行渲染或处理。
2. 基本用法
<!-- 子组件(Child.vue) -->
<template>
<div class="child">
<h3>{
{ title }}</h3>
<p v-if="showDesc">{
{ description }}</p>
</div>
</template>
<script>
export default {
// 定义接收的 props
props: {
// 基础用法:指定类型
title: String,
// 完整配置:类型、默认值、验证
description: {
type: String,
default: '默认描述', // 默认值
required: false // 非必填
},
showDesc: {
type: Boolean,
default: true,
// 自定义验证器(可选)
validator: (value) => {
return typeof value === 'boolean'
}
}
}
}
</script>
<!-- 父组件使用 -->
<template>
<Child
title="用户信息"
description="这是一个用户卡片"
:showDesc="true"
/>
</template>
3. 关键特性
- 单向数据流:父组件数据更新会自动同步到子组件,但子组件不能直接修改 props(需通过
$emit通知父组件修改)。 - 类型验证:可指定 props 的类型(String/Number/Boolean/Object 等),Vue 会在开发环境校验,避免数据类型错误。
- 默认值与必填项:通过配置
default和required,让组件更健壮。
二、插槽(Slot):父组件向子组件传递「结构」的通道
插槽是 Vue 中父组件向子组件传递HTML结构、组件或带逻辑的模板的方式,本质是「模板的分发」,让父组件能直接控制子组件内部的部分 UI 结构。
1. 核心作用
- 让父组件能自定义子组件的部分内容(如卡片的主体内容、弹窗的按钮组等)。
- 解决了「子组件框架固定,但部分内容需要父组件灵活定制」的问题。
2. 常见类型及用法
(1)默认插槽(匿名插槽)
子组件中未命名的 <slot>,父组件传递的内容会默认填充到这里。
<!-- 子组件(Card.vue) -->
<template>
<div class="card">
<div class="card-header">卡片标题</div>
<!-- 默认插槽:父组件内容会替换这里 -->
<slot>
<!-- 插槽默认内容:如果父组件没传内容,显示这个 -->
<p>默认卡片内容</p>
</slot>
</div>
</template>
<!-- 父组件使用 -->
<template>
<Card>
<!-- 传递给默认插槽的内容 -->
<p>这是父组件自定义的卡片内容</p>
<button @click="handleClick">父组件的按钮</button>
</Card>
</template>
(2)具名插槽
当子组件有多个需要定制的区域(如头部、主体、底部),可用 name 区分不同插槽。
<!-- 子组件(Card.vue) -->
<template>
<div class="card">
<!-- 头部插槽 -->
<slot name="header"></slot>
<!-- 主体默认插槽 -->
<slot></slot>
<!-- 底部插槽 -->
<slot name="footer"></slot>
</div>
</template>
<!-- 父组件使用 -->
<template>
<Card>
<!-- 填充头部插槽(语法:#header 等价于 v-slot:header) -->
<template #header>
<h3>自定义标题</h3>
</template>
<!-- 填充默认插槽 -->
<p>主体内容...</p>
<!-- 填充底部插槽 -->
<template #footer>
<button>确定</button>
<button>取消</button>
</template>
</Card>
</template>
(3)作用域插槽(带数据的插槽)
子组件可向插槽传递数据(子传父),让父组件根据子组件的数据自定义渲染方式。
<!-- 子组件(List.vue) -->
<template>
<ul>
<li v-for="item in list" :key="item.id">
<!-- 向插槽传递 item 数据 -->
<slot :item="item" :index="index">
<!-- 默认渲染方式 -->
{
{ item.name }}
</slot>
</li>
</ul>
</template>
<script>
export default {
data() {
return {
list: [
{ id: 1, name: '苹果', price: 5 },
{ id: 2, name: '香蕉', price: 3 }
]
}
}
}
</script>
<!-- 父组件使用:接收子组件数据并自定义渲染 -->
<template>
<List>
<!-- slotProps 接收子组件传递的所有数据 -->
<template #default="slotProps">
<div>
名称:{
{ slotProps.item.name }},
价格:{
{ slotProps.item.price }}元
</div>
</template>
</List>
</template>
三、Props 与插槽的核心区别
| 维度 | Props | 插槽(Slot) |
|---|---|---|
| 传递内容 | 数据(字符串、数字、对象等) | 结构(HTML、组件、带逻辑的模板) |
| 渲染控制权 | 子组件控制(按固定逻辑渲染) | 父组件控制(自定义渲染方式) |
| 数据流向 | 父 → 子(单向) | 可双向(子组件可通过作用域插槽传数据给父) |
| 适用场景 | 传递配置或业务数据 | 传递自定义 UI 或动态内容 |
四、使用场景总结
用 Props 的场景:
- 传递配置信息(如标题、提示文本、是否显示某元素的开关)。
- 传递业务数据(如列表数据、用户信息、状态标识)。
- 子组件的渲染逻辑固定,只需要父组件提供“原材料”。
例如:一个按钮组件,通过 type(primary/danger)和 disabled 控制样式和状态,这些配置适合用 props 传递。
用插槽的场景:
- 传递自定义 UI 结构(如卡片内容、弹窗的表单、列表项的布局)。
- 子组件有多个可定制区域(如头部、底部、操作区),需用具名插槽区分。
- 子组件提供数据,但父组件需要自定义数据的渲染方式(作用域插槽)。
例如:一个对话框组件,标题、内容、底部按钮都需要父组件自定义,这时用插槽更合适。
五、总结
Props 和插槽不是对立关系,而是互补关系:
- Props 解决“子组件需要什么数据”的问题;
- 插槽解决“子组件的这部分内容该长什么样”的问题。
在实际开发中,合理结合两者(如用 props 传递配置,用插槽传递内容),能设计出既灵活又易用的组件。
六、例子
<!-- Card.vue - 一个通用的卡片组件,展示 props 和插槽的用法 -->
<template>
<div class="card" :class="variantClass">
<!-- 卡片头部 - 使用 props 传递标题和描述 -->
<div class="card-header">
<!-- 标题通过 props 传入 -->
<h2>{
{ title }}</h2>
<!-- 描述信息通过 props 传入,使用 v-if 控制显示 -->
<p v-if="description" class="card-description">{
{ description }}</p>
</div>

最低0.47元/天 解锁文章
18万+

被折叠的 条评论
为什么被折叠?



