一文详析vue3 Props的用法(父传子)
作者:fishmemory7sec
在 Vue 3 中,Props(属性)用于在组件之间传递数据。
Props的作用
- 传参:Props 允许父组件向子组件传递数据。
- 类型检查:Vue 允许在定义 Props 时指定数据的类型,这有助于在开发过程中进行类型检查,提前发现潜在的类型错误。
- 设置默认值:可以为 Props 设置默认值。当父组件没有传递该 Prop 时,子组件会使用默认值进行渲染。
Vue 中的 Props 遵循单向数据流原则,即父组件向子组件传递数据,子组件不能直接修改父组件传递过来的 Prop。
父组件传值给子组件
在父组件的模板中,可以使用属性绑定的方式将数据传递给子组件(静态传值):
<ChildComponent title="哈嘿" />
可以使用变量或表达式来动态地传递 Props:
<!-- 根据一个变量的值动态传入 --> <ChildComponent :title="title" /> <!-- 根据一个更复杂表达式的值动态传入 --> <ChildComponent :title="someCondition ? titleA : titleB" />
可以使用 v-bind
动态绑定所有的 props:
可以将一个对象传递给 v-bind
,这个对象的属性将被用作组件的 props。
<template> <ChildComponent v-bind="obj" /> </template> <script setup lang="ts"> import ChildComponent from './ChildComponent.vue'; import { reactive } from 'vue' let obj = reactive({ message: 'Hello', count: 5 }) </script>
<ChildComponent v-bind="obj" />
等价于:
<ChildComponent :message="message" :count="count" />
声明 Props
- 使用
defineProps
函数:- 在
<script setup>
语法中,可以直接使用defineProps
函数来声明组件接收的 Props。 - 在 Vue 3 中,基于类型的声明通常用于组合式 API 。
- 在
<script setup lang="ts"> import { defineProps } from 'vue'; // 字符串数组形式 const props = defineProps(['message', 'count']); </script>
在这个例子中,声明了两个 Props:message
和count
。
可以使用对象字面量的方式来声明 Props:
<script setup lang="ts"> import { defineProps } from 'vue'; const props = defineProps({ message: String, count: Number, }); </script>
在这个例子中,声明了两个 Props:message
是字符串类型,count
是数字类型。通过类型注解,TypeScript 可以在开发过程中进行类型检查,确保传入的 props 值符合预期的类型。
在 <script setup>
语法中,使用 defineProps
函数结合类型注解来进行基于类型的声明。
如果项目没有使用 TypeScript,就无法使用基于类型的声明进行声明。
基于类型的声明可以在开发过程中利用 TypeScript 的类型检查机制,提前发现类型错误。
- 在选项式 API 中(没有使用
<script setup>
语法糖),props 是通过props
选项进行声明的。- 必须要用
props
选项声明组件接收的Props,在setup()
的props
参数里,才有Props数据。 - 在 Vue 3 中,运行时声明通常用于选项式 API。
- 必须要用
export default { props: ['message'], setup(props) { // setup() 接收 props 作为第一个参数 console.log(props.message) } }
必须要用props
选项声明组件接收的Props。如果没有 props: ['message']
:
export default { setup(props) { // 报错: 类型“LooseRequired<{} & {}>”上不存在属性“message”。 console.log(props.message) } }
通过在组件选项对象中直接定义 props
属性来进行运行时声明:
export default { props: { message: String, count: Number, }, setup() {} // 组件的其他选项和逻辑 };
在这个例子中,message
被声明为字符串类型的 Prop,count
被声明为数字类型的 Prop。这种方式在运行时,Vue 会根据声明的类型对传入的 Props 进行验证。
运行时声明通常在不需要进行严格类型检查或者需要更灵活地处理 Props 的情况下使用。它主要依赖于 Vue 的运行时机制来处理 Props 的传递和验证。
传递给 defineProps()
的参数和提供给 props
选项的值是相同的,本质上都是在使用 prop
选项。
Props 的验证
Props 验证的主要目的是确保组件接收到的数据符合预期的格式和类型。
类型验证
- 可以指定 Props 的类型,如
String
、Number
、Boolean
、Array
、Object
等。 - 也可以是函数类型
Function
、自定义类型。
<script setup lang="ts"> import { defineProps } from 'vue'; // 定义一个接口作为自定义类型 interface MyCustomType{ property1: string; property2: number; } const props = defineProps({ name: String, age: Number, hobbies: Array, address: Object, customType: MyCustomType, // 自定义类型 onButtonClick: Function // 函数类型 }); </script>
确保父组件传递给子组件的Props值与声明的类型一致,避免类型错误。
必需性
- 可以指定 Props 是否为必需,如果为必需,则必须在父组件中传入。
<script setup lang="ts"> import { defineProps } from 'vue'; const props = defineProps({ message: { type: String, required: true } }); </script>
在这个例子中,message
是必需的 Prop,如果父组件没有传递 message
,则会抛出警告。
自定义验证
- 可以使用 validator 函数进行自定义验证。
<script setup lang="ts"> import { defineProps } from "vue"; let props = defineProps({ b: { type: Number, validator: (value: number) => { return value >= 0; // 自定义验证,确保宽度非负 } } }) console.log(props) </script>
在这个例子中,b
使用了自定义验证函数,确保b
的值不为负数。
在父组件传入b = -10
,浏览器控制台输出警告:
默认值
可以为 Props 设置默认值,当父组件没有传递该 Prop 时,子组件会使用默认值进行渲染。
<script setup lang="ts"> import { defineProps } from 'vue'; const props = defineProps({ message: { type: String, default: 'Hello, Vue 3!', }, count: { type: Number, default: 0, }, }); </script>
在这个例子中,message
的默认值为 'Hello, Vue 3!'
,count
的默认值为 0
。
withDefaults
在 Vue 3 中,withDefaults
是一个用于为 defineProps
定义的 props 设置默认值的函数。
withDefaults
参数说明:
withDefaults
的第一个参数是defineProps
的返回值,它表示组件接收的 props 对象。- 第二个参数是一个对象,其中的键对应于 props 的名称,值是相应的默认值。
- 默认值是通过函数返回的。
假设有一个组件,定义了一些 props,并且希望为其中一些 props 设置默认值:
<template> <div>{{ props.message }}</div> <div>{{ props.count }}</div> </template> <script setup lang="ts"> import { defineProps, withDefaults } from 'vue'; // 定义了一个接口 PropsInter 来描述组件的 props 结构 interface PropsInter { message: string; count: number; obj: { a: number; b: number; }; arr: string[]; } const props = withDefaults(defineProps<PropsInter>(), { message: 'Hello, Vue 3!', count: 0, obj: () => { return { a: 10, b: 5 } }, arr: () => return [ 'item1', 'item2' ] }); </script>
Boolean 类型转换
当声明为 Boolean
类型的 props
时,有特别的类型转换规则,以便更贴近原生的 boolean attributes 的行为。
- 当一个 prop 明确被声明为布尔类型时,真值会被转换为
true
,假值会被转换为false
:- 当父组件传递一个真值(如
true
、字符串"true"
、数字1
等)时,该 prop 将被转换为true
。 - 当父组件传递一个假值(如
false
、字符串"false"
、数字0
、空字符串""
、null
、undefined
等)时,该 prop 将被转换为false
。
- 当父组件传递一个真值(如
在子组件中:
defineProps({ disabled: Boolean })
在父组件中:
<!-- 等同于传入 :disabled="true" --> <MyComponent disabled /> <!-- 等同于传入 :disabled="false" --> <MyComponent />
- 当一个 prop 被声明为允许多种类型时,声明顺序会影响 Boolean 转换规则是否适用:
- 当同时允许字符串和布尔类型时,如果布尔类型出现在字符串类型之前,Boolean 转换规则才适用。
const props = defineProps({ myProp: [Boolean, String], });
如果父组件传递 "true"
,这个值将被转换为布尔值 true
。如果传递 "false"
,将被转换为布尔值 false
。其他字符串值将保持为字符串。
但是,如果声明顺序是 [String, Boolean]
,那么 "true"
和 "false"
将被视为字符串,而不会进行布尔类型的转换。
这种边缘情况可能会导致在不同的声明顺序下,相同的输入值被解释为不同的类型。
使用 Props
在父组件的模板中传值给子组件:
<ChildComponent title="哈嘿" />
在子组件的模板中,可以直接使用定义的 Props:
<template> <div>{{ title }}</div> </template> <script setup lang="ts" name="ChildComponent"> import { defineProps } from "vue"; defineProps(['title']) // console.log(title) // 报错:找不到名称“title”。 </script>
在模板中,可以直接使用title
。
console.log(title)
报错:找不到名称“title”。
这是因为title
并没有被直接声明为一个可用的变量。
在 Vue 3 的 <script setup>
中,defineProps
的返回值是一个包含传入的 props 的对象。
如果想要在组件中使用 title
,需要通过defineProps
的返回值来读取:
<template> <div>{{ props.title }}</div> </template> <script setup lang="ts" name="ChildComponent"> import { defineProps } from "vue"; // 只接收title属性 let props = defineProps(['title']) console.log(props) if(props.title) { console.log(props.title) } </script>
defineProps
返回一个对象 props
,props
对象中包含了 title
属性,可以通过 props.title
的方式来访问和使用这个 prop 的值。
defineProps
的返回值是一个包含组件接收的 props
的只读对象:
- 返回的
props
对象是只读的。 - 返回的
props
对象包含了组件所有接收的东西:父组件传递给当前组件的所有被组件明确声明为 props 的属性。- 必须在
defineProps
中显式的接收父组件传递的属性。假如父组件传了几个属性,比如<ChildComponent title="哈嘿" id="id12345" />
,子组件defineProps(['title'])
只接收title
属性,不会接收id
属性。
- 必须在
- 在组件内部不能直接修改
props
对象的属性值。
实战演练
在types/index.ts
定义一个PersonInter
接口:
// 定义一个接口,用于限制对象的具体属性 // 接口在 TypeScript 中用于定义一种契约,确保实现该接口的对象都具有相同的结构。 export interface PersonInter { id: string; name: string; age: number; }
在父组件有一个person
传递给子组件:
<template> <div> <ChildComponent :personList="personList" /> </div> </template> <script setup lang="ts" name="Person"> // 在 TypeScript 中,import type 语法用于仅导入类型信息而不导入实际的值或函数。 // 使用 import type 导入的类型信息仅在类型检查时使用,不会在运行时产生任何影响。 import type { PersonInter } from '@/types'; import { reactive } from 'vue'; import ChildComponent from './ChildComponent.vue'; let personList = reactive<PersonInter>({ id: 'person001', name: 'John', age: 18 }); </script>
子组件:
<template> <div>{{ person.name }}</div> </template> <script setup lang="ts" name="ChildComponent"> import type { PersonInter } from '@/types'; import { defineProps } from "vue"; // 只接收person let props = defineProps(['person']) console.log(props) // 接收person+类型限制 let props = defineProps<{ person: PersonInter }>() // 接收person+类型限制+限制必要性+默认值 // ?表示不必传,父组件可传可不传,如果父组件不传,则使用默认值 // 指定默认值需要使用withDefaults withDefaults(defineProps<{ person?: PersonInter }>(), { person: () => { return { id: 'person000', name: 'Alice', age: 18 } } }) </script>
let props = defineProps(['person'])
只接收person
,对person
没有任何限制。父组件可以给person
赋任意类型的值。defineProps<{ person: PersonInter }>()
: 通过泛型参数指定了props
的结构。具体来说,定义了一个名为person
的 prop,其类型为PersonInter
。defineProps<{ person?: PersonInter }>()
:- 使用泛型参数指定了
props
的结构。定义了一个名为person
的 prop,其类型为PersonInter
。 - 后面的问号
?
表示这个 prop 是可选的,即父组件可以选择是否传递这个 prop。
- 使用泛型参数指定了
总结
到此这篇关于vue3 Props用法(父传子)的文章就介绍到这了,更多相关vue3 Props用法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!