Vue深入组件:Props 详解1_vue中的props是什么意思
Props 是组件之间传递数据的主要方式,父组件通过 props 向子组件传递数据,子组件则需要显式声明接收的 props。本章将从 props 的声明、使用、传递规则到校验等方面,全面解析 Vue 中的 props 机制。
Props 声明
子组件必须显式声明它所接受的 props,这样 Vue 才能区分传入的是 props 还是透传 attribute(透传 attribute 指未被组件声明为 props 的属性,将在后续章节专门讨论)。根据组件是否使用 <script setup>,声明方式有所不同。
基础声明方式
1. 使用 <script setup>时:defineProps()宏
在 <script setup> 中,Vue 提供了 defineProps() 宏来声明 props,无需导入即可直接使用。声明后会返回一个包含 props 数据的响应式对象。
<script setup>
// 以字符串数组形式声明:接收名为 "foo" 的 prop
const props = defineProps(['foo'])
// 访问 prop 的值
console.log(props.foo)
</script>
2. 不使用 <script setup>时:props选项
若未使用 <script setup>,需在组件选项中通过 props 选项声明,同时 setup() 函数会将 props 作为第一个参数传入。
export default {
// 以字符串数组形式声明
props: ['foo'],
setup(props) {
// setup 中通过参数访问 props
console.log(props.foo)
}
}
两种方式的本质一致:defineProps() 的参数与 props 选项的值完全等效,最终都会被处理为组件的 props 配置。
对象形式声明(推荐)
除了字符串数组,更推荐使用对象形式声明 props。对象的 key 是 prop 名称,值可以是类型构造函数(用于类型校验)或更详细的校验配置对象。
// 使用 <script setup> 时
defineProps({
title: String, // 声明 "title" 为 String 类型
likes: Number // 声明 "likes" 为 Number 类型
})
// 不使用 <script setup> 时
export default {
props: {
title: String,
likes: Number
}
}
对象形式的优势:
- 自带文档:清晰展示每个 prop 的预期类型,便于团队协作。
- 类型校验:若父组件传递错误类型,Vue 会在浏览器控制台抛出警告,帮助快速定位问题。
TypeScript 类型标注声明
若搭配 TypeScript 使用 <script setup>,可通过类型标注声明 props,语法更简洁,且能享受 TypeScript 的类型检查。
<script setup lang="ts">
// 通过类型标注声明:title 和 likes 为可选属性(? 表示可选)
defineProps<{
title?: string
likes?: number
}>()
</script>
类型标注不仅支持基础类型,还支持接口、泛型等复杂类型,更符合 TypeScript 项目的开发习惯。关于类型标注的更多细节,可参考 Vue 官方文档的“组件 props 类型标注”章节。
响应式 Props 解构
Vue 的响应系统通过属性访问跟踪状态依赖(例如在计算属性中访问 props.foo 时,foo 会被标记为依赖项,当 foo 变化时重新执行相关逻辑)。因此,解构 props 时需注意响应性的保持。
Vue 3.5+ 对解构的优化
在 Vue 3.4 及以下版本中,若直接解构 props(如 const { foo } = defineProps(['foo'])),foo 会成为一个非响应式的常量,后续 foo 变化时,依赖它的逻辑(如 watchEffect)不会重新执行。
而在 Vue 3.5+ 中,编译器会自动优化:当在同一个 <script setup> 中解构 defineProps 返回的 props 时,会将解构变量自动转换为 props.xxx 的访问形式,从而保持响应性。
// Vue 3.5+ 中,以下代码会被编译器优化
const { foo } = defineProps(['foo'])
watchEffect(() => {
// 实际等价于 console.log(props.foo),foo 变化时会重新执行
console.log(foo)
})
解构时的默认值
可通过 JavaScript 原生的默认值语法为解构的 props 设置默认值,尤其适合基于类型标注的 props 声明。
// 为可选 prop "foo" 设置默认值 "hello"
const { foo = 'hello' } = defineProps<{ foo?: string }>()
若需在 IDE 中区分解构的 props 和普通变量,Vue 的 VSCode 扩展提供了“解构 props 内联提示”设置,开启后可在解构变量旁显示 props 标识。
解构 props 传递到函数的注意事项
若将解构的 prop 直接传递到 watch 等需要响应式数据源的函数中,会因传递的是“值”而非“响应式引用”导致无法正常工作。
const { foo } = defineProps(['foo'])
// 错误:传递的是 foo 的当前值,而非响应式数据源
watch(foo, (newVal) => { ... })
正确做法:将解构的 prop 包装在 getter 函数中,让 Vue 能跟踪到 props 的访问。
const { foo } = defineProps(['foo'])
// 正确:通过 getter 函数访问 foo,保持响应性
watch(() => foo, (newVal) => { ... })
同理,当需将解构的 prop 传递到外部函数并保持响应性时,也应使用 getter 包装:
// 外部函数可通过调用 getter 跟踪 prop 变化
useComposable(() => foo)