一、Props 基础概念
Props 是 Vue 3 中实现组件间通信的核心机制之一,允许父组件向子组件传递数据。它遵循单向数据流原则,即数据只能从父组件流向子组件,子组件不能直接修改父组件传递的 Props,确保了数据流向的清晰可预测。
二、静态 Props 的传递方法
静态 Props 是指传递给子组件的固定值,不会随父组件数据变化而改变。
2.1 基本用法
在父组件中直接通过属性名传递固定值:
vue
<!-- 父组件 Parent.vue -->
<template>
<ChildComponent title="欢迎来到 Vue 3 世界" />
</template>
<script setup>
import ChildComponent from './ChildComponent.vue'
</script>
子组件通过 defineProps 接收:
vue
<!-- 子组件 ChildComponent.vue -->
<template>
<h1>{{ title }}</h1>
</template>
<script setup>
const props = defineProps(['title'])
</script>
2.2 静态 Props 的特点
- 传递的值是固定的字符串或基本类型
- 不需要使用
v-bind指令 - 适用于传递不会变化的配置信息
三、动态 Props 的传递方法
动态 Props 允许我们将父组件的响应式数据传递给子组件,当父组件数据变化时,子组件会自动更新。
3.1 基本用法
使用 v-bind 指令(或简写 :)绑定父组件的数据:
vue
<!-- 父组件 Parent.vue -->
<template>
<ChildComponent :message="parentMessage" :count="parentCount" />
<button @click="parentCount++">增加计数</button>
</template>
<script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'
const parentMessage = ref('这是动态传递的消息')
const parentCount = ref(0)
</script>
子组件接收并使用:
vue
<!-- 子组件 ChildComponent.vue -->
<template>
<p>{{ message }}</p>
<p>当前计数:{{ count }}</p>
</template>
<script setup>
const props = defineProps(['message', 'count'])
</script>
3.2 动态 Props 的高级用法
3.2.1 传递对象
可以一次性传递多个属性:
vue
<!-- 父组件 -->
<template>
<ChildComponent :user="userInfo" />
</template>
<script setup>
import { reactive } from 'vue'
import ChildComponent from './ChildComponent.vue'
const userInfo = reactive({
name: '张三',
age: 25,
email: 'zhangsan@example.com'
})
</script>
子组件接收:
vue
<!-- 子组件 -->
<template>
<div>
<p>姓名:{{ user.name }}</p>
<p>年龄:{{ user.age }}</p>
<p>邮箱:{{ user.email }}</p>
</div>
</template>
<script setup>
const props = defineProps(['user'])
</script>
3.2.2 传递数组
vue
<!-- 父组件 -->
<template>
<ChildComponent :items="list" />
</template>
<script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'
const list = ref(['Vue', 'React', 'Angular'])
</script>
往期文章归档
-
Vue 3组合式API中ref与reactive的核心响应式差异及使用最佳实践是什么? - cmdragon's Blog
-
Vue 3组合式API中ref与reactive的核心响应式差异及使用最佳实践是什么? - cmdragon's Blog
-
Vue 3中watch侦听器的正确使用姿势你掌握了吗?深度监听、与watchEffect的差异及常见报错解析 - cmdragon's Blog
-
Vue 3中reactive函数如何通过Proxy实现响应式?使用时要避开哪些误区? - cmdragon's Blog
-
快速入门Vue的v-model表单绑定:语法糖、动态值、修饰符的小技巧你都掌握了吗? - cmdragon's Blog
-
只给表子集建索引?用函数结果建索引?PostgreSQL这俩操作凭啥能省空间又加速? - cmdragon's Blog
-
想抓PostgreSQL里的慢SQL?pg_stat_statements基础黑匣子和pg_stat_monitor时间窗,谁能帮你更准揪出性能小偷? - cmdragon's Blog
-
PostgreSQL 查询慢?是不是忘了优化 GROUP BY、ORDER BY 和窗口函数? - cmdragon's Blog
-
PostgreSQL选Join策略有啥小九九?Nested Loop/Merge/Hash谁是它的菜? - cmdragon's Blog
-
PostgreSQL索引选B-Tree还是GiST?"瑞士军刀"和"多面手"的差别你居然还不知道? - cmdragon's Blog
-
PostgreSQL处理SQL居然像做蛋糕?解析到执行的4步里藏着多少查询优化的小心机? - cmdragon's Blog
-
PostgreSQL备份不是复制文件?物理vs逻辑咋选?误删还能精准恢复到1分钟前? - cmdragon's Blog
-
PostgreSQL里的PL/pgSQL到底是啥?能让SQL从"说目标"变"讲步骤"? - cmdragon's Blog
-
PostgreSQL UPDATE语句怎么玩?从改邮箱到批量更新的避坑技巧你都会吗? - cmdragon's Blog
-
PostgreSQL 17安装总翻车?Windows/macOS/Linux避坑指南帮你搞定? - cmdragon's Blog
-
能当关系型数据库还能玩对象特性,能拆复杂查询还能自动管库存,PostgreSQL凭什么这么香? - cmdragon's Blog
-
如何用GitHub Actions为FastAPI项目打造自动化测试流水线? - cmdragon's Blog
免费好用的热门在线工具
子组件接收并渲染:
vue
<!-- 子组件 -->
<template>
<ul>
<li v-for="item in items" :key="item">{{ item }}</li>
</ul>
</template>
<script setup>
const props = defineProps(['items'])
</script>
3.3 动态 Props 流程图
markdown
父组件响应式数据变化
↓
v-bind 自动更新传递给子组件的 Props
↓
子组件接收到更新后的 Props
↓
子组件模板自动重新渲染
四、TypeScript 类型约束
在 Vue 3 中使用 TypeScript 可以为 Props 添加严格的类型约束,提高代码的可维护性和可靠性。
4.1 使用接口定义 Props 类型
vue
<!-- 子组件 ChildComponent.vue -->
<template>
<div>
<p>标题:{{ title }}</p>
<p>计数:{{ count }}</p>
<p>用户信息:{{ user.name }} - {{ user.age }}</p>
</div>
</template>
<script setup lang="ts">
interface User {
name: string
age: number
}
interface Props {
title: string
count: number
user: User
// 可选属性
description?: string
}
const props = defineProps<Props>()
</script>
4.2 为 Props 添加默认值
使用 withDefaults 函数为 Props 设置默认值:
vue
<script setup lang="ts">
interface User {
name: string
age: number
}
interface Props {
title: string
count: number
user: User
description?: string
}
const props = withDefaults(defineProps<Props>(), {
title: '默认标题',
count: 0,
user: () => ({
name: '默认用户',
age: 18
}),
description: '这是一个默认描述'
})
</script>
4.3 类型约束的优势
- 编译时类型检查,提前发现错误
- 更好的 IDE 自动补全和类型提示
- 清晰的组件 API 文档
- 减少运行时错误
五、课后 Quiz
问题 1:如何区分静态 Props 和动态 Props?它们的使用场景有什么不同?
答案解析:
- 静态 Props 直接传递固定值,不需要使用
v-bind,适用于传递不会变化的配置信息,如组件标题、静态文本等。 - 动态 Props 使用
v-bind绑定父组件的响应式数据,当父组件数据变化时,子组件会自动更新,适用于需要动态展示的数据,如列表数据、用户信息等。
问题 2:在 Vue 3 + TypeScript 项目中,如何为 Props 定义类型并设置默认值?
答案解析:
- 首先定义接口来描述 Props 的类型:
typescript
interface Props {
title: string
count: number
description?: string
}
- 使用
withDefaults函数结合defineProps来设置默认值:
typescript
const props = withDefaults(defineProps<Props>(), {
title: '默认标题',
count: 0,
description: '默认描述'
})
六、常见报错解决方案
错误 1:[Vue warn]: Missing required prop: "title"
原因分析: 子组件声明了必填的 Props,但父组件没有传递。 解决办法:
- 父组件中传递对应的 Props:
vue
<ChildComponent title="我是必填标题" />
- 或者在子组件中将该 Props 设置为可选:
typescript
interface Props {
title?: string
}
错误 2:[Vue warn]: Invalid prop: type check failed for prop "count". Expected Number, got String with value "10".
原因分析: 传递的 Props 类型与子组件声明的类型不匹配。 解决办法:
- 确保传递的值类型正确,使用
v-bind传递数字类型:
vue
<ChildComponent :count="10" />
- 或者在子组件中调整类型定义:
typescript
interface Props {
count: number | string
}
错误 3:Cannot assign to read only property 'title' of object '#<Object>'
原因分析: 子组件尝试直接修改父组件传递的 Props,违反了单向数据流原则。 解决办法:
- 如果需要修改数据,应该在子组件中定义本地响应式数据,将 Props 作为初始值:
typescript
const props = defineProps<{ title: string }>()
const localTitle = ref(props.title)
- 或者通过触发父组件的方法来修改数据:
vue
<!-- 子组件 -->
<button @click="$emit('update:title', '新标题')">修改标题</button>
vue
<!-- 父组件 -->
<ChildComponent :title="parentTitle" @update:title="parentTitle = $event" />