Vue: Component Props
2022-03-24
0. Intro
1. Props Declaration
Vue component 需要 explicit props declaration,这样 Vue 才知道传入的参数中哪些应该被当做 fallthrough attributes(这个概念之后会讨论)。
Example - use array syntax
export default {
props: ['foo'],
created() {
// props are exposed on `this`
console.log(this.foo)
}
}
Example 2 - use object syntax (推荐)
key: prop name
value: constructor function of the expected type
export default {
props: {
title: String,
likes: Number
}
}
2. Prop Passing Details
Prop Name Casing
用 camelCase 来命名,这样的命名可以避免我们在使用 property key 的时候加引号,还可以直接在 template expression 里面 reference 它们,因为它们都是 valid JS identifiers。
export default {
props: {
greetingMessage: String
}
}
<span>{{ greetingMessage }}</span>
虽然我们可以在 pass props to a child component 的时候用 camelCase,但惯例是用 kebab-case。在 HTML 里面写 component tag 的时候,一般用 PascalCase,因为这样易读性更强,可以区分 Vue component from native elements。
<MyComponent greeting-message="hello" />
Static vs. Dynamic Props
静态值:
<BlogPost title="My journey with Vue" />
动态值:用 v-bind
或者 :
<!-- Dynamically assign the value of a variable -->
<BlogPost :title="post.title" />
<!-- Dynamically assign the value of a complex expression -->
<BlogPost :title="post.title + ' by ' + post.author.name" />
Passing Different Value Types
Example - Number
<!-- Even though `42` is static, we need v-bind to tell Vue that -->
<!-- this is a JavaScript expression rather than a string. -->
<BlogPost :likes="42" />
<!-- Dynamically assign to the value of a variable. -->
<BlogPost :likes="post.likes" />
Example - Boolean
<!-- Including the prop with no value will imply `true`. -->
<BlogPost is-published />
<!-- Even though `false` is static, we need v-bind to tell Vue that -->
<!-- this is a JavaScript expression rather than a string. -->
<BlogPost :is-published="false" />
<!-- Dynamically assign to the value of a variable. -->
<BlogPost :is-published="post.isPublished" />
Example - Array
<!-- Even though the array is static, we need v-bind to tell Vue that -->
<!-- this is a JavaScript expression rather than a string. -->
<BlogPost :comment-ids="[234, 266, 273]" />
<!-- Dynamically assign to the value of a variable. -->
<BlogPost :comment-ids="post.commentIds" />
Example - Object
<!-- Even though the object is static, we need v-bind to tell Vue that -->
<!-- this is a JavaScript expression rather than a string. -->
<BlogPost
:author="{
name: 'Veronica',
company: 'Veridian Dynamics'
}"
/>
<!-- Dynamically assign to the value of a variable. -->
<BlogPost :author="post.author" />
3. Binding Multiple Properties Using an Object
假如我们要把一个 object 的所有 properties 都传入作为 props,可以直接用 v-bind
without argument 来实现。
If you want to pass all the properties of an object as props, you can use
v-bind
without an argument (v-bind
instead of:prop-name
).
Example
Template 里面的两种写法效果是一样的,第一种是传入所有 properties。
<BlogPost v-bind="post" />
<BlogPost :id="post.id" :title="post.title" />
export default {
data() {
return {
post: {
id: 1,
title: 'My Journey with Vue'
}
}
}
}
4. One-Way Data Flow
所有 props 都是 child property 和 parent 之间的 one-way-down-binding。
当 parent property 更新时,child 也会更新,但是 child 更新不会让 parent 更新。
parent -> child
为什么?
这可以避免 child 意外地改变 parent state,two-way-binding 会让你的 app data flow 难以理解。
Don’t mutate child component’s prop
每次 parent component update,child component 的所有 props 都会更新到最新的 value,这意味着你不应该尝试改变一个 child component 里面的 prop,假如你这么做的话会看到一个 console warning message - props are readonly!
Example
export default {
props: ['foo'],
created() {
// ❌ warning, props are readonly!
this.foo = 'bar'
}
}
如何正确地「改变 prop」value
当你想要改变 prop 的时候通常有两种情况:
-
The prop is used to pass in an initial value; the child component wants to use it as a local data property afterwards.
- 解决方案:define local data property that uses the prop as its initial value
-
The prop is passed in as a raw value that needs to be transformed.
- 解决方案:define a computed property using the prop’s value
Example - local data property
export default {
props: ['initialCounter'],
data() {
return {
// counter only uses this.initialCounter as the initial value;
// it is disconnected from future prop updates.
counter: this.initialCounter
}
}
}
Example - computed property
export default {
props: ['size'],
computed: {
// computed property that auto-updates when the prop changes
normalizedSize() {
return this.size.trim().toLowerCase()
}
}
}
改变 Object/Array Props
Child component 可以 mutate object/array’s nested properties,因为在 JS 里面,objects and arrays 是通过 reference 传入的,所以 Vue 也没有必要去阻止这种 mutation。
问题:会改变 parent state,但是比较难发现,因此导致 data flow 不明确。
best practice:不要在 child 里面改这些 value,除非设计本身就是让 child 和 parent tightly coupled。大部分情况下,我们都应该让 child emit an event to let the parent perform the mutation.
5. Prop Validation
Components 可以明确地写出对 props 的要求,比如 type,是否 require。
假如传入的值没有达到要求,Vue 会有警告提示。
默认情况下,所有 props 都是 optional;假如没有传入值,就是 undefined
;假如有 default
value,就会在 override undefined。
Note that props are validated before a component instance is created, so instance properties (e.g.
data
,computed
, etc.) will not be available insidedefault
orvalidator
functions.
Example
export default {
props: {
// Basic type check
// (`null` and `undefined` values will allow any type)
propA: Number,
// Multiple possible types
propB: [String, Number],
// Required string
propC: {
type: String,
required: true
},
// Number with a default value
propD: {
type: Number,
default: 100
},
// Object with a default value
propE: {
type: Object,
// Object or array defaults must be returned from
// a factory function. The function receives the raw
// props received by the component as the argument.
default(rawProps) {
// default function receives the raw props object as argument
return { message: 'hello' }
}
},
// Custom validator function
propF: {
validator(value) {
// The value must match one of these strings
return ['success', 'warning', 'danger'].includes(value)
}
},
// Function with a default value
propG: {
type: Function,
// Unlike object or array default, this is not a factory function - this is a function to serve as a default value
default() {
return 'Default function'
}
}
}
}
Runtime Type Checks
Type 可以有以下几种:
String
Number
Boolean
Array
Object
Date
Function
Symbol
也可以用 custom class 或者 constructor function 来做 type,此时的 runtime check 就是 instanceof
check。
class Person {
constructor(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
}
export default {
props: {
author: Person
}
}
6. Boolean Casting
Boolean 类型的 Props 有 special casting rules,用来模拟原生 boolean attributes。
Example
<!-- equivalent of passing :disabled="true" -->
<MyComponent disabled />
<!-- equivalent of passing :disabled="false" -->
<MyComponent />
export default {
props: {
disabled: Boolean
}
}
Example - multiple types allowed
The casting rules for Boolean
will apply regardless of type appearance order.
export default {
props: {
disabled: [Boolean, Number]
}
}