Props 是一种特别的 attributes,你需要在组件上声明注册,然后才能识别成props,而不是普通的attributes。
本篇主要讲解props的用法,以及传递后的porps值如何保持其响应性。
如果一个 prop 的名字很长,应使用 camelCase 形式,因为它们是合法的 JavaScript 标识符,可以直接在模板的表达式中使用,也可以避免在作为属性 key 名时必须加上引号。
在组合式写法中,需要使用defineProps 宏进行props声明。
<!-- BlogPost.vue -->
<script setup>
defineProps(['title'])
</script>
<template>
<h4>{{ title }}</h4>
</template>
defineProps 是一个仅
const props = defineProps(['title'])
console.log(props.title)
在选项式组件中,需要在props属性上声明,它接收一个字符串数组或对象。
export default {
props: ['foo'],
setup(props) {
// setup() 接收 props 作为第一个参数
console.log(props.foo)
}
}
当你需要定制props时,需要使用对象的形式进行声明,这样你可以进行prop传递值校验、prop类型预设、默认值、是否必须传递等配置。
当 prop 的校验失败后,Vue 会抛出一个控制台警告 (在开发模式下)。
defineProps() 宏中的参数不可以访问
defineProps({
// 基础类型检查
// (给出 `null` 和 `undefined` 值则会跳过任何类型检查)
propA: Number,
// 多种可能的类型
propB: [String, Number],
// 必传,且为 String 类型
propC: {
type: String,
required: true
},
// Number 类型的默认值
propD: {
type: Number,
default: 100
},
// 对象类型的默认值
propE: {
type: Object,
// 对象或数组的默认值
// 必须从一个工厂函数返回。
// 该函数接收组件所接收到的原始 prop 作为参数。
default(rawProps) {
return { message: 'hello' }
}
},
// 自定义类型校验函数
// 在 3.4+ 中完整的 props 作为第二个参数传入
propF: {
validator(value, props) {
// The value must match one of these strings
return ['success', 'warning', 'danger'].includes(value)
}
},
// 函数类型的默认值
propG: {
type: Function,
// 不像对象或数组的默认,这不是一个
// 工厂函数。这会是一个用来作为默认值的函数
default() {
return 'Default function'
}
}
})
一些补充细节:
在prop传递时,推荐使用 kebab-case 形式(camelCase形式也行):
<MyComponent greeting-message="hello" />
静态值一般是传递字符串,就像普通attributes赋值一样。
<BlogPost title="My journey with Vue" />
动态值指使用v-bind动态绑定的prop传递值,可以传不同类型的值(字符串、布尔型、数字、对象等)。
<BlogPost :title="post.title" />
<BlogPost :title="post.title + ' by ' + post.author.name" />
<BlogPost
:author="{
name: 'Veronica',
company: 'Veridian Dynamics'
}"
/>
<BlogPost :author="post.author" />
<BlogPost :comment-ids="[234, 266, 273]" />
<BlogPost :comment-ids="post.commentIds" />
需要整体绑定多个prop时,可以使用v-bind:
const post = {
id: 1,
title: 'My Journey with Vue'
}
<BlogPost v-bind="post" />
等价于:
<BlogPost :id="post.id" :title="post.title" />
所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递。
当对象或数组作为 props 被传入时,虽然子组件无法更改 props 绑定,但仍然可以更改对象或数组内部的值。这是因为 JavaScript 的对象和数组是按引用传递,而对 Vue 来说,禁止这样的改动,虽然可能生效,但有很大的性能损耗,比较得不偿失。
在组件中是不能直接修改prop的值的,否则会报错。
若想修改需要对prop的值进行一层转接:
const props = defineProps(['initialCounter'])
// 计数器只是将 props.initialCounter 作为初始值
// 像下面这样做就使 prop 和后续更新无关了
const counter = ref(props.initialCounter)
const props = defineProps(['size'])
// 该 prop 变更时计算属性也会自动更新
const normalizedSize = computed(() => props.size.trim().toLowerCase())
直接使用一直由响应性的。
prop在解构使用后会失去响应性,在解构使用时我们需要使用toRefs将props包裹,以创建一个和prop同源的响应性值:
<script setup lang="ts">
import { toRef, toRefs } from 'vue'
const props = defineProps<{
msg: string
age: number
}>()
const msg1 = toRef(() => props.msg)
const { msg, age } = toRefs(props)
console.log(msg.value, age.value)
</script>
想单个的话可以使用toRef,或者直接使用props.prop。
由于toRef和toRefs创建的是和props同源的ref值,所以在组件中也是只读ref值。
结束了。