Props
This chapter assumes that you have looked at Component Fundamentals . If you don't know what a component is yet, please read this chapter first.
Props declaration
A component needs to explicitly declare the props it accepts, so that Vue can know which props are passed in from the outside and which are transparent attributes (we will discuss transparent attributes in a special chapter ) .
In single-file components used <script setup>
, props can be defineProps()
declared using macros:
vue
<script setup>
const props = defineProps(['foo'])
console.log(props.foo)
</script>
In unused <script setup>
components, props can be declared using the props option:
js
export default {
props: ['foo'],
setup(props) {
// setup() 接收 props 作为第一个参数
console.log(props.foo)
}
}
Note that the parameters passed to defineProps()
and the value provided to props
the option are the same, and the prop option is actually used behind the two declaration methods.
In addition to using an array of strings to declare props, you can also use the form of objects:
js
// 使用 <script setup>
defineProps({
title: String,
likes: Number
})
js
// 非 <script setup>
export default {
props: {
title: String,
likes: Number
}
}
For each property in an object declaration, the key is the name of the prop, and the value is the constructor function for that prop's expected type. For example, if a prop's value is required to be number
a type, use Number
a constructor as its declared value.
The props declaration in the form of an object can not only serve as a component's documentation to some extent, but also throw a warning in the browser console if other developers pass the wrong type when using your component. We'll discuss more details about prop validation further down in this chapter .
If you're using TypeScript <script setup>
, you can also use type annotations to declare props:
vue
<script setup lang="ts">
defineProps<{
title?: string
likes?: number
}>()
</script>
See Component props type annotations for more details on type-based declarations .
Passing prop details
Prop name format
If the name of a prop is very long, camelCase should be used, because they are legal JavaScript identifiers, can be used directly in template expressions, and can also avoid having to add quotes when used as property key names.
js
defineProps({
greetingMessage: String
})
template
<span>{
{ greetingMessage }}</span>
Although in theory you can also use camelCase when passing props to child components (except when using DOM templates ), in practice, in order to align with HTML attributes, we usually write it as kebab-case:
template
<MyComponent greeting-message="hello" />
We recommend using PascalCase for component names , because it improves the readability of the template and helps us distinguish Vue components from native HTML elements. However, for passing props, there is not much advantage to using camelCase, so we recommend a writing style closer to HTML.
Static vs. Dynamic Props
By now, you've seen a lot of props with static values like this:
template
<BlogPost title="My journey with Vue" />
Correspondingly, there are props that use v-bind
or abbreviate :
for dynamic binding:
template
<!-- 根据一个变量的值动态传入 -->
<BlogPost :title="post.title" />
<!-- 根据一个更复杂表达式的值动态传入 -->
<BlogPost :title="post.title + ' by ' + post.author.name" />
Pass different value types
In the above two examples, we only passed string values, but in fact any type of value can be passed as the value of props.
Number
template
<!-- 虽然 `42` 是个常量,我们还是需要使用 v-bind -->
<!-- 因为这是一个 JavaScript 表达式而不是一个字符串 -->
<BlogPost :likes="42" />
<!-- 根据一个变量的值动态传入 -->
<BlogPost :likes="post.likes" />
Boolean
template
<!-- 仅写上 prop 但不传值,会隐式转换为 `true` -->
<BlogPost is-published />
<!-- 虽然 `false` 是静态的值,我们还是需要使用 v-bind -->
<!-- 因为这是一个 JavaScript 表达式而不是一个字符串 -->
<BlogPost :is-published="false" />
<!-- 根据一个变量的值动态传入 -->
<BlogPost :is-published="post.isPublished" />
Array
template
<!-- 虽然这个数组是个常量,我们还是需要使用 v-bind -->
<!-- 因为这是一个 JavaScript 表达式而不是一个字符串 -->
<BlogPost :comment-ids="[234, 266, 273]" />
<!-- 根据一个变量的值动态传入 -->
<BlogPost :comment-ids="post.commentIds" />
Object
template
<!-- 虽然这个对象字面量是个常量,我们还是需要使用 v-bind -->
<!-- 因为这是一个 JavaScript 表达式而不是一个字符串 -->
<BlogPost
:author="{
name: 'Veronica',
company: 'Veridian Dynamics'
}"
/>
<!-- 根据一个变量的值动态传入 -->
<BlogPost :author="post.author" />
Use one object to bind multiple props
If you want to pass in all properties of an object as props, you can use v-bind with no parameters , that is, only use v-bind
not :prop-name
. For example, here is an post
object:
js
const post = {
id: 1,
title: 'My Journey with Vue'
}
and the following template:
template
<BlogPost v-bind="post" />
And this is actually equivalent to:
template
<BlogPost :id="post.id" :title="post.title" />
unidirectional data flow
All props follow the principle of one-way binding . Props change due to the update of the parent component, and naturally flow the new state down to the child component without passing it backwards. This prevents child components from accidentally modifying the state of the parent component, otherwise the data flow of the application can easily become confusing and difficult to understand.
In addition, every time the parent component is updated, all props in the child components will be updated to the latest values, which means that you should not change a prop in the child component. If you do, Vue will throw you a warning on the console:
js
const props = defineProps(['foo'])
// ❌ 警告!prop 是只读的!
props.foo = 'bar'
The need to change a prop usually comes from two scenarios:
-
prop is used to pass in the initial value; the child component wants to use it later as a local data property . In this case, it is best to define a new local data property and get the initial value from props:
jsconst props = defineProps(['initialCounter']) // 计数器只是将 props.initialCounter 作为初始值 // 像下面这样做就使 prop 和后续更新无关了 const counter = ref(props.initialCounter)
-
Further transformations are required on the passed in prop values . In this case, it is better to define a computed property based on the prop value:
jsconst props = defineProps(['size']) // 该 prop 变更时计算属性也会自动更新 const normalizedSize = computed(() => props.size.trim().toLowerCase())
Change props
When an object or array is passed in as props, although subcomponents cannot change the props binding, they can still change the value inside the object or array. This is because JavaScript objects and arrays are passed by reference. For Vue, such changes are prohibited. Although it may take effect, it will cause a large performance loss, which is not worth the candle.
The main drawback of this change is that it allows child components to affect the state of the parent component in some non-obvious way, possibly making the data flow more difficult to understand in the future. In best practice, you should avoid such changes as much as possible, unless the parent-child components are tightly coupled by design. In most scenarios, child components should throw an event to notify the parent component of changes.
Prop Validation
Vue components can declare validation requirements for incoming props in more detail. For example, the type declaration we have seen above, if the incoming value does not meet the type requirements, Vue will throw a warning in the browser console to remind the user. This is useful when developing components for use by other developers.
To declare validations for props, you can defineProps()
provide the macro with an object with props validation options, for example:
js
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' }
}
},
// 自定义类型校验函数
propF: {
validator(value) {
// The value must match one of these strings
return ['success', 'warning', 'danger'].includes(value)
}
},
// 函数类型的默认值
propG: {
type: Function,
// 不像对象或数组的默认,这不是一个
// 工厂函数。这会是一个用来作为默认值的函数
default() {
return 'Default function'
}
}
})
TIP
defineProps()
Parameters in the macro cannot access <script setup>
other variables defined in the macro, because the entire expression will be moved to the outer function at compile time.
Some additional details:
-
All props are optional by default unless declared otherwise
required: true
. -
Boolean
Optional props that are notundefined
. -
Boolean
Unpassed props of type will be converted tofalse
. This can be changed by setting itdefault
- eg: set todefault: undefined
will behave the same as props of non-boolean type. -
If a value is declared , it will be changed to the value when
default
the prop's value is resolved to , regardless of whether the prop was not passed or explicitly specified .undefined
undefined
default
Vue throws a console warning (in development mode) when the prop validation fails.
If a type-based prop declaration is used , Vue will do its best to compile according to the prop's type annotation at runtime. For example, defineProps<{ msg: string }>
would be compiled as { msg: { type: String, required: true }}
.
Runtime type checking
Validation options type
can be the following native constructors:
String
Number
Boolean
Array
Object
Date
Function
Symbol
In addition, type
it can also be a custom class or constructor, and Vue will instanceof
check whether the type matches. For example the following class:
js
class Person {
constructor(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
}
You can use it as a prop type:
js
defineProps({
author: Person
})
Vue will instanceof Person
check author
whether the value of the prop is Person
an instance of the class.
Boolean type conversion
In order to be closer to the behavior of native boolean attributes, Boolean
props declared as types have special type conversion rules. Take <MyComponent>
for example a component with the following declaration:
js
defineProps({
disabled: Boolean
})
This component can be used like this:
template
<!-- 等同于传入 :disabled="true" -->
<MyComponent disabled />
<!-- 等同于传入 :disabled="false" -->
<MyComponent />
When a prop is declared to allow multiple types, for example:
js
defineProps({
disabled: [Boolean, Number]
})
Boolean
The special conversion rules for the types are applied regardless of the order in which the types are declared .