1、声明:
- 在使用 <script setup> 的单文件组件中,props 可以使用 defineProps() 宏来声明:
//字符串数组声明
<script setup>
const props = defineProps(['foo'])console.log(props.foo)
</script>//对象形式的声明
defineProps({title: String,likes: Number
})
- -在没有使用 <script setup> 的组件中,props 可以使用 props 选项来声明:
//字符串数组声明
export default {props: ['foo'],setup(props) {// setup() 接收 props 作为第一个参数console.log(props.foo)}
}//对象形式的声明
export default {props: {title: String,likes: Number}
}
2、解构响应式Props
<template><div>{{ foo }}</div></template><script setup>// 在3.4及以下,foo是一个常量,不会响应式更新;父组件传递的数据更改时,foo不会改变// 在3.5及以上,foo是响应式的,Vue会自动添加props.前缀const { foo } = defineProps(['foo']);</script>
3、将解构的props传递到函数中
当我们将解构的prop传递到watch函数中去:
const { foo } = defineProps(['foo'])1、watch(foo, /* ... */) //错误
2、watch(() => foo, /* ... */) //正确
第一种方式错误是因为:给 watch 传递的是一个值而不是响应式数据源,而watch监听的应该是一个可变的数据,不然没有意义。而且Vue 的编译器会捕捉这种情况并发出警告
第二种方式正确是因为:确保了对foo(或props.foo)的依赖收集,按照Vue的响应式原理,能够在foo变化时触发对应的回调
4、传递prop的细节
Prop名字格式:
//子组件接收数据时用小驼峰
defineProps({greetingMessage: String
})//父组件向子组件传递props时使用羊肉串( kebab-case 形式)
<MyComponent greeting-message="hello" />
静态 vs. 动态 Props
//静态
<BlogPost title="My journey with Vue" />//动态
<!-- 根据一个变量的值动态传入 -->
<BlogPost :title="post.title" /><!-- 根据一个更复杂表达式的值动态传入 -->
<BlogPost :title="post.title + ' by ' + post.author.name" />
使用一个对象绑定多个 prop
如果你想要将一个对象的所有属性都当作 props 传入,你可以使用没有参数的 v-bind,即只使用 v-bind 而非 :prop-name
//假设这是要传入的一个对象
const post = {id: 1,title: 'My Journey with Vue'
}
<BlogPost v-bind="post" /> ==== <BlogPost :id="post.id" :title="post.title" />
5、单向数据流
所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递
注:子组件中数据的更新是由父组件数据更新后流下来的,而不是在子组件中去更改prop,prop只是可读的
想更改prop的一般是两个场景:
1、prop 被用于传入初始值;而子组件想在之后将其作为一个局部数据属性
const props = defineProps(['initialCounter'])// 计数器只是将 props.initialCounter 作为初始值
// 像下面这样做就使 prop 和后续更新无关了
const counter = ref(props.initialCounter)
2、需要对传入的 prop 值做进一步的转换,最好是利用计算属性:
const props = defineProps(['size'])// 该 prop 变更时计算属性也会自动更新
const normalizedSize = computed(() => props.size.trim().toLowerCase())
6、更改对象 / 数组类型的 props
当对象或数组作为 props 被传入时,虽然子组件无法更改 props 绑定,但仍然可以更改对象或数组内部的值
原因:JavaScript 的对象和数组是按引用传递,对 Vue 来说,阻止这种更改需要付出的代价异常昂贵。
弊端:它允许了子组件以某种不明显的方式影响父组件的状态,可能会使数据流在将来变得更难以理解
解决措施:在大多数场景下,子组件应该抛出一个事件来通知父组件做出改变
7、细节
- 所有 prop 默认都是可选的,除非声明了 required: true
- 除 Boolean 外的未传递的可选 prop 将会有一个默认值 undefined
- Boolean 类型的未传递 prop 将被转换为 false。这可以通过为它设置 default 来更改——例如:设置为 default: undefined 将与非布尔类型的 prop 的行为保持一致。
- 如果该类型是必传但可为 null 的,你可以用一个包含 null 的数组语法:
defineProps({id: {type: [String, null],required: true}
})