Props(Properties)是父组件向子组件传递数据的方式。子组件通过props选项声明它可以接收的数据。
<!-- 父组件 -->
<template>
<child-component
title="欢迎学习Vue.js"
:count="totalCount"
:is-active="true"
:items="itemList"
/>
</template>
<script>
export default {
data() {
return {
totalCount: 42,
itemList: ['Vue', 'React', 'Angular']
}
}
}
</script>
// 子组件
export default {
// 声明props
props: {
title: String,
count: Number,
isActive: Boolean,
items: Array
},
template: `
<div>
<h2>{{ title }}</h2>
<p>计数: {{ count }}</p>
<p v-if="isActive">状态: 激活</p>
<ul>
<li v-for="item in items" :key="item">{{ item }}</li>
</ul>
</div>
`
}
Vue支持多种Prop类型,可以提供类型检查:
| 类型 | 语法示例 | 描述 | 验证示例 |
|---|---|---|---|
| String | title: String |
字符串类型 | "Hello World" |
| Number | age: Number |
数字类型 | 25, 3.14 |
| Boolean | isActive: Boolean |
布尔类型 | true, false |
| Array | items: Array |
数组类型 | [1, 2, 3], ["a", "b"] |
| Object | user: Object |
对象类型 | {name: "John", age: 30} |
| Function | callback: Function |
函数类型 | () => console.log("hi") |
| Symbol | key: Symbol |
Symbol类型 | Symbol('id') |
多种类型:
prop: [String, Number] - 可以是字符串或数字
|
|||
通过详细的验证配置,可以确保组件接收正确的数据:
验证数据类型
是否必须传递
未传递时的默认值
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数返回
default: function() {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function(value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
},
// 带有默认值的函数
propG: {
type: Function,
// 与对象或数组默认值不同,这不是一个工厂函数
default: function() {
return 'Default function'
}
}
}
props: {
username: {
type: String,
required: true // 必须传递此prop
}
}
使用场景:关键数据,没有默认值的情况
props: {
// 基本类型默认值
size: {
type: String,
default: 'medium'
},
// 对象/数组默认值必须是工厂函数
config: {
type: Object,
default: () => ({
color: 'blue',
size: 'md'
})
}
}
注意:对象和数组必须使用工厂函数
props: {
status: {
type: String,
validator: function(value) {
// 必须是指定的值之一
return ['pending', 'active', 'archived'].includes(value)
},
default: 'pending'
},
// 更复杂的验证
score: {
type: Number,
validator: function(value) {
return value >= 0 && value <= 100
}
}
}
用途:限制输入值的范围或格式
Props可以通过不同方式从父组件传递:
<!-- 静态Prop(字符串) -->
<my-component title="静态标题"></my-component>
<!-- 静态Prop(数字) -->
<my-component :count="42"></my-component>
<!-- 静态Prop(布尔值) -->
<my-component :is-visible="true"></my-component>
<my-component is-visible></my-component> <!-- 等价于 :is-visible="true" -->
<template>
<my-component
:title="dynamicTitle"
:count="userCount"
:is-active="isUserActive"
/>
</template>
<script>
export default {
data() {
return {
dynamicTitle: '动态标题',
userCount: 100,
isUserActive: true
}
}
}
</script>
<!-- 传递整个对象 -->
<user-profile :user="userObject"></user-profile>
<!-- 等价于: -->
<user-profile
:name="userObject.name"
:age="userObject.age"
:email="userObject.email"
></user-profile>
<!-- 布尔Prop的特殊用法 -->
<!-- 以下三种写法是等价的 -->
<my-modal visible></my-modal>
<my-modal :visible="true"></my-modal>
<my-modal visible="true"></my-modal>
<!-- 传递false必须使用动态绑定 -->
<my-modal :visible="false"></my-modal>
<!-- 以下写法会传递字符串"false"而不是布尔值false -->
<!-- ❌ 错误写法 -->
<my-modal visible="false"></my-modal>
:),Vue会将其解析为truefalse,必须使用:prop-name="false"语法Vue遵循特定的命名约定来处理Props:
// 在JavaScript中使用camelCase(驼峰命名法)
props: {
greetingText: String,
userName: String,
isUserActive: Boolean
}
// 在HTML中使用kebab-case(短横线分隔命名法)
<!-- 父组件模板 -->
<my-component
greeting-text="Hello"
user-name="John"
:is-user-active="true"
></my-component>
创建一个带有完整Props验证的用户卡片组件:
// UserCard.vue - 子组件
export default {
name: 'UserCard',
props: {
// 必需的用户信息
user: {
type: Object,
required: true,
validator: function(value) {
// 验证user对象是否包含必需字段
return value &&
typeof value.name === 'string' &&
typeof value.email === 'string'
}
},
// 可选的显示选项
showAvatar: {
type: Boolean,
default: true
},
avatarSize: {
type: String,
default: 'medium',
validator: function(value) {
return ['small', 'medium', 'large'].includes(value)
}
},
showActions: {
type: Boolean,
default: false
},
status: {
type: String,
default: 'active',
validator: function(value) {
return ['active', 'inactive', 'pending'].includes(value)
}
},
// 数字验证
rating: {
type: Number,
default: 0,
validator: function(value) {
return value >= 0 && value <= 5
}
},
// 自定义类名
customClass: {
type: String,
default: ''
},
// 回调函数
onEdit: {
type: Function,
default: () => {}
},
onDelete: {
type: Function,
default: () => {}
}
},
computed: {
// 根据status计算样式类
statusClass() {
return {
'active': this.status === 'active',
'inactive': this.status === 'inactive',
'pending': this.status === 'pending'
}
},
// 根据avatarSize计算尺寸
avatarSizeClass() {
return `avatar-${this.avatarSize}`
}
},
methods: {
handleEdit() {
this.onEdit(this.user.id)
},
handleDelete() {
this.onDelete(this.user.id)
}
},
template: `
<div class="user-card" :class="[customClass, statusClass]">
<div class="user-header">
<div v-if="showAvatar" class="user-avatar" :class="avatarSizeClass">
{{ user.name.charAt(0).toUpperCase() }}
</div>
<div class="user-info">
<h3 class="user-name">{{ user.name }}</h3>
<p class="user-email">{{ user.email }}</p>
<div class="user-rating">
<span v-for="i in 5" :key="i"
:class="i <= rating ? 'star-filled' : 'star-empty'">
★
</span>
</div>
</div>
</div>
<div v-if="showActions" class="user-actions">
<button @click="handleEdit" class="btn-edit">
编辑
</button>
<button @click="handleDelete" class="btn-delete">
删除
</button>
</div>
</div>
`
}
<!-- 父组件使用示例 -->
<template>
<div>
<!-- 使用默认值 -->
<user-card :user="currentUser" />
<!-- 自定义所有选项 -->
<user-card
:user="currentUser"
:show-avatar="false"
avatar-size="large"
:show-actions="true"
status="active"
:rating="4"
custom-class="highlight-card"
:on-edit="handleEditUser"
:on-delete="handleDeleteUser"
/>
<!-- 使用对象绑定 -->
<user-card :user="{
name: '张三',
email: 'zhangsan@example.com'
}" />
</div>
</template>
<script>
import UserCard from './UserCard.vue'
export default {
components: {
UserCard
},
data() {
return {
currentUser: {
id: 1,
name: '李四',
email: 'lisi@example.com'
}
}
},
methods: {
handleEditUser(userId) {
console.log('编辑用户:', userId)
},
handleDeleteUser(userId) {
console.log('删除用户:', userId)
}
}
}
</script>
<!-- 父组件 -->
<child-component :title.sync="pageTitle"></child-component>
<!-- 等价于 -->
<child-component
:title="pageTitle"
@update:title="pageTitle = $event"
></child-component>
<!-- 子组件 -->
<button @click="$emit('update:title', newTitle)">
更新标题
</button>
<!-- 使用v-bind传递对象的所有属性 -->
<user-card v-bind="userData"></user-card>
<!-- 等价于 -->
<user-card
:name="userData.name"
:age="userData.age"
:email="userData.email"
></user-card>
// ❌ 错误做法:直接修改Prop
this.title = '新标题'
// ✅ 正确做法:使用本地数据
data() {
return {
localTitle: this.title
}
}
// ✅ 正确做法:触发事件通知父组件
this.$emit('update-title', '新标题')
// ✅ 正确做法:使用计算属性
computed: {
formattedTitle() {
return this.title.toUpperCase()
}
}
尝试为以下组件定义Props:
// 为这个产品卡片组件定义合适的Props
Vue.component('ProductCard', {
props: {
// TODO: 在这里定义Props
// 应该包括:产品信息、价格、图片、状态等
},
template: `
<div class="product-card">
<img :src="image" :alt="name">
<h3>{{ name }}</h3>
<p>{{ description }}</p>
<div class="price">¥{{ price }}</div>
<div v-if="inStock" class="in-stock">有货</div>
<div v-else class="out-of-stock">缺货</div>
</div>
`
});
提示:考虑必需性、类型验证、默认值等。