Props 与 Emits
约 661 字大约 2 分钟
布欧-Lewyon
2026-05-15
首页 › Vue › 组件与通信 › Props 与 Emits
组件最基础的通信方式——父传子用 Props,子传父用 Emits。
Props
声明方式
<!-- Child.vue -->
<script>
export default {
props: {
title: String, // 简单类型声明
count: { type: Number, default: 0 }, // 带选项
user: { // 复杂校验
type: Object,
default: () => ({ name: '默认用户' }),
validator: (val) => val.name.length > 0
},
tags: { type: Array, default: () => [] },
visible: Boolean,
onChange: Function // 虽然可以传函数,但推荐 emits
}
}
</script>
<template>
<h3>{{ title }}</h3>
<p>计数: {{ count }}</p>
<p>用户: {{ user.name }}</p>
</template>Props 类型
| 类型 | 说明 | 默认值注意 |
|---|---|---|
String | 字符串 | default: '' |
Number | 数字 | default: 0 |
Boolean | 布尔 | default: false |
Array | 数组 | 必须用函数 default: () => [] |
Object | 对象 | 必须用函数 default: () => ({}) |
Function | 函数 | default: () => {} |
Array 和 Object 的 default 必须用工厂函数,否则多个组件实例会共享同一个引用。
单向数据流
Props 遵循单向数据流——子组件不能直接修改 props:
<!-- ❌ 错误 -->
<script>
export default {
props: ['count'],
methods: {
increment() { this.count++ } // ❌ 不要直接修改 props
}
}
</script>正确的做法:要么用 emit 通知父组件改,要么用本地数据接收:
<script>
export default {
props: ['initialCount'],
data() {
return {
localCount: this.initialCount // ✅ 用本地 data 接收
}
}
}
</script>Emits
声明与触发
<!-- Child.vue -->
<script>
export default {
emits: ['update', 'delete'], // 声明(推荐,便于文档和校验)
methods: {
handleUpdate(val) {
this.$emit('update', val) // 触发事件
},
handleDelete(id) {
this.$emit('delete', id)
}
}
}
</script><!-- Parent.vue -->
<template>
<Child @update="onUpdate" @delete="onDelete" />
</template>Emits 校验
<script>
export default {
emits: {
// 无校验
update: null,
// 带校验(返回 false 表示校验失败,开发环境有警告)
delete: (id) => {
if (typeof id !== 'number') {
console.warn('delete 事件的参数应为数字')
return false
}
return true
}
}
}
</script>完整示例:分页组件
<!-- Pagination.vue -->
<script>
export default {
props: {
total: { type: Number, required: true },
pageSize: { type: Number, default: 10 },
current: { type: Number, default: 1 }
},
emits: ['page-change'],
computed: {
totalPages() {
return Math.ceil(this.total / this.pageSize)
}
},
methods: {
go(page) {
if (page < 1 || page > this.totalPages) return
this.$emit('page-change', page)
}
}
}
</script>
<template>
<div class="pagination">
<button :disabled="current <= 1" @click="go(current - 1)">上一页</button>
<span>{{ current }} / {{ totalPages }}</span>
<button :disabled="current >= totalPages" @click="go(current + 1)">下一页</button>
</div>
</template>Props 的细节技巧
Boolean 与 String 的转换
<script>
export default {
props: {
disabled: [Boolean, Number]
// 使用 <Button disabled="disabled"> → disabled = true(Boolean 优先)
}
}
</script>无声明 Props($attrs)
没有在 props 中声明的属性会自动透传到根元素:
<template>
<input class="my-input" /> <!-- 父组件传的 placeholder 会自动透传到 <input> -->
</template>小结
| 概念 | 要点 |
|---|---|
| Props | 父传子,单向数据流,不可修改 |
| Emits | 子传父,用 $emit 触发 |
| Props 校验 | type、default、validator、required |
| Array/Object default | 必须用工厂函数 () => [] |
| 修改 Props | 用本地 data / computed 接收,或 emit 通知父组件 |
上一节:事件处理与条件/列表渲染 下一节:Slot 内容分发
