Skip to content

声明 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 额外的删除再创建,消耗性能。