声明 props
ts
defineProps(['foo'])
defineProps({ title: String, likes: Number })//类型严格
defineProps<{
title?: string //可选
likes?: number
}>()- 组件采用大驼峰,props 采用kebab-case;
- 子组件声明到的 props 都是只读的,如果要确实要修改,可以将传入的 props 作为初始值初始化一个 ref 对象;如果要对传入的 props 做进一步转化处理,定义一个与其相关的计算属性即可。
prop 校验
在接受 props 的时候校验传参类型:
ts
defineProps({
// 基础类型检查
// (给出 `null` 和 `undefined` 值则会跳过任何类型检查)
propA: Number,
// 多种可能的类型
propB: [String, Number],
// 必传,且为 String 类型
propC: {
type: String,
required: true
},
// 必传但可为 null 的字符串
propD: {
type: [String, null],
required: true
},
// Number 类型的默认值
propE: {
type: Number,
default: 100
},
// 对象类型的默认值
propF: {
type: Object,
// 对象或数组的默认值
// 必须从一个工厂函数返回。
// 该函数接收组件所接收到的原始 prop 作为参数。
default(rawProps) {
return { message: 'hello' }
}
},
// 自定义类型校验函数
// 在 3.4+ 中完整的 props 作为第二个参数传入
propG: {
validator(value, props) {
// The value must match one of these strings
return ['success', 'warning', 'danger'].includes(value)
}
},
// 函数类型的默认值
propH: {
type: Function,
// 不像对象或数组的默认,这不是一个
// 工厂函数。这会是一个用来作为默认值的函数
default() {
return 'Default function'
}
}
})也可以用一个类进行 props 检验,Vue 会检查传入的 props 是否为该类的一个实例(是不是从这个类 new 出来的):
ts
class Person {
constructor(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
}
defineProps({
author: Person
})除非声明 required 否则默认可选。 校验失败在开发模式下会传出控制台警告。
boolean 类型转换
处理那些看起来是 boolean,但其实有可能是字符串或者是数字的值。 在标签上:
ts
<!-- 等同于传入 :disabled="true" -->
<MyComponent disabled />
<!-- 等同于传入 :disabled="false" -->
<MyComponent />如果 Boolean 类型在 String 类型之前,那么只有 true、false、1、0 会被当作 Boolean 处理,其他的值(如 "true" 或 "false")会被当作 String 处理。
传递 props
- props 向子组件的传递都是单向的 传递对象的所有属性的简写:
ts
<BlogPost v-bind="post" />
-->无参v-bind等价于
<BlogPost :id="post.id" :title="post.title" />子组件不能直接修改 props 的绑定数据源,但是可以直接修改绑定数据源内部的属性,但最好不要这么做,而是抛出一个事件让父组件接收并做出相应的更改。
父组件向子组件传递数据
- 如果需要绑定 JS 表达式,则需要使用
v-bind可以给 Vue 组件标签传属性a,实现父组件向子组件的传值:
html
<template>
<person a="haha"/>
</template>
<script lang="ts" setup name="App">
import { reactive, ref } from 'vue';
import person from './components/person.vue'
import { type Persons } from '@/types'
let per = reactive<Persons>([
{name : '1', age : 20, id : '1'},
{name : '2', age : 30, id : '2'},
{name : '3', age : 40, id : '3'},
])
</script>子组件使用 defineProps 拿到数据,包装在一个对象中作为返回值(define 开头的函数都是宏函数,不需要 import):
ts
let x = defineProps(['a'])
console.log(x.a)下面是一个传递对象的例子:
html
<template>
<person a="haha" :personList="per"/>
</template>
<script lang="ts" setup name="App">
import { reactive, ref } from 'vue';
import person from './components/person.vue'
import { type Persons } from '@/types'
let per = reactive<Persons>([
{name : '1', age : 20, id : '1'},
{name : '2', age : 30, id : '2'},
{name : '3', age : 40, id : '3'},
])
</script>指定父组件传递类型:
ts
defineProps<{personList:Persons}>()
defineProps<{personList?:Persons}>()//可以传递也可以不传递这个参数
let y = withDefaults(defineProps<{personList?:Persons}>(),{
personList: ()=> [{id:'none', name: 'none', age: 0}]
})//默认值v-for 遍历渲染
html
<template>
<div class="number">
<ul>
<li v-for="item in x.personList">{{ item.name }} -- {{ item.age }}</li>
</ul>
</div>
</template>推荐写成:
html
<li v-for="item in x.personList" :key="item.id">{{ item.name }} -- {{ item.age }}</li>key 是元素的标识,具有唯一性,diff 算法同层比较会根据 key 判断 DOM 是在原基础上更新,还是删除再重新创建,若 key 不唯一,会造成 DOM 额外的删除再创建,消耗性能。