Vue 3中的props是用于接收父组件传递的数据的属性。在Vue 3中,props的声明发生了一些改变:

使用props选项来声明props。之前的版本中使用props属性来声明,但在Vue 3中改为使用props选项。通过TypeScript或Flow来静态类型检查props。Vue 3允许使用TypeScript或Flow类型系统来对props进行静态类型检查,以提供更好的开发体验和类型安全。使用PropTypes来提供类型检查。虽然Vue官方并未提供内置的PropTypes,但可以使用第三方库如vue-types来实现类似的类型检查。

目录

Props 声明

传递 prop 的细节

1:Prop 名字格式

2:静态 vs. 动态 Prop

3:传递不同的值类型

3.1:Number

3.2:Boolean

3.3:Array

3.4:Object

4:使用一个对象绑定多个 prop

单向数据流

更改对象 / 数组类型的 props

Prop 校验

运行时类型检查

Boolean 类型转换

Props 声明

一个组件需要显式声明它所接受的 props,这样 Vue 才能知道外部传入的哪些是 props,哪些是透传 attribute (关于透传 attribute,我们会在专门的章节中讨论)。

在使用 

在没有使用 

传递 prop 的细节

1:Prop 名字格式

如果一个 prop 的名字很长,应使用 camelCase 形式,因为它们是合法的 JavaScript 标识符,可以直接在模板的表达式中使用,也可以避免在作为属性 key 名时必须加上引号。

defineProps({

greetingMessage: String

})

{{ greetingMessage }}

虽然理论上你也可以在向子组件传递 props 时使用 camelCase 形式 (使用 DOM 内模板时例外),但实际上为了和 HTML attribute 对齐,我们通常会将其写为 kebab-case 形式:

对于组件名我们推荐使用 PascalCase,因为这提高了模板的可读性,能帮助我们区分 Vue 组件和原生 HTML 元素。然而对于传递 props 来说,使用 camelCase 并没有太多优势,因此我们推荐更贴近 HTML 的书写风格。

2:静态 vs. 动态 Prop

至此,你已经见过了很多像这样的静态值形式的 props:

相应地,还有使用 v-bind 或缩写 : 来进行动态绑定的 props:

3:传递不同的值类型

在上述的两个例子中,我们只传入了字符串值,但实际上任何类型的值都可以作为 props 的值被传递。

3.1:Number

3.2:Boolean

3.3:Array

3.4:Object

:author="{

name: 'Veronica',

company: 'Veridian Dynamics'

}"

/>

4:使用一个对象绑定多个 prop

如果你想要将一个对象的所有属性都当作 props 传入,你可以使用没有参数的 v-bind,即只使用 v-bind 而非 :prop-name。例如,这里有一个 post 对象:

const post = {

id: 1,

title: 'My Journey with Vue'

}

以及下面的模板:

而这实际上等价于:

单向数据流

所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递。这避免了子组件意外修改父组件的状态的情况,不然应用的数据流将很容易变得混乱而难以理解。

另外,每次父组件更新后,所有的子组件中的 props 都会被更新到最新值,这意味着你不应该在子组件中去更改一个 prop。若你这么做了,Vue 会在控制台上向你抛出警告:

const props = defineProps(['foo'])

// ❌ 警告!prop 是只读的!

props.foo = 'bar'

导致你想要更改一个 prop 的需求通常来源于以下两种场景:

1:prop 被用于传入初始值;而子组件想在之后将其作为一个局部数据属性。在这种情况下,最好是新定义一个局部数据属性,从 props 上获取初始值即可:

const props = defineProps(['initialCounter'])

// 计数器只是将 props.initialCounter 作为初始值

// 像下面这样做就使 prop 和后续更新无关了

const counter = ref(props.initialCounter)

2:需要对传入的 prop 值做进一步的转换。在这种情况中,最好是基于该 prop 值定义一个计算属性:

const props = defineProps(['size'])

// 该 prop 变更时计算属性也会自动更新

const normalizedSize = computed(() => props.size.trim().toLowerCase())

更改对象 / 数组类型的 props

当对象或数组作为 props 被传入时,虽然子组件无法更改 props 绑定,但仍然可以更改对象或数组内部的值。这是因为 JavaScript 的对象和数组是按引用传递,而对 Vue 来说,禁止这样的改动,虽然可能生效,但有很大的性能损耗,比较得不偿失。

这种更改的主要缺陷是它允许了子组件以某种不明显的方式影响父组件的状态,可能会使数据流在将来变得更难以理解。在最佳实践中,你应该尽可能避免这样的更改,除非父子组件在设计上本来就需要紧密耦合。在大多数场景下,子组件应该抛出一个事件来通知父组件做出改变。

Prop 校验

Vue 组件可以更细致地声明对传入的 props 的校验要求。比如我们上面已经看到过的类型声明,如果传入的值不满足类型要求,Vue 会在浏览器控制台中抛出警告来提醒使用者。这在开发给其他开发者使用的组件时非常有用。

要声明对 props 的校验,你可以向 defineProps() 宏提供一个带有 props 校验选项的对象,例如:

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'

}

}

})

defineProps() 宏中的参数不可以访问