一、为什么需要组件间通信?
在Vue应用中,组件是独立的功能模块。要让它们协同工作,需要:
- 父组件向子组件传递数据(如用户信息)
- 子组件向父组件反馈状态(如表单提交)
- 父组件直接调用子组件方法(如控制弹窗显示)
Vue3通过三个组合式API提供了简洁的解决方案:
二、defineProps:接收父组件的数据
1. 作用
让子组件接收父组件传递的值(类似Vue2的props
选项)
2. 快速上手
vue
<!-- 子组件 Child.vue -->
<template>
<h2>欢迎,{{ username }}</h2>
</template>
<script setup>
// 声明接收的props
const props = defineProps({
username: String // 接收字符串类型的username
})
</script>
vue
<!-- 父组件 Parent.vue -->
<template>
<Child username="张三" /> <!-- 传递数据 -->
</template>
3. 三个关键点
-
默认值 :使用
withDefaults
vueconst props = withDefaults(defineProps({ size: { type: Number, default: 16 } }), { size: 18 // 最终默认值为18 })
-
类型校验 :基础类型校验
vuedefineProps({ age: { type: Number, required: true } // 必填项 })
-
单向数据流:子组件不能直接修改props,需通过事件通知父组件修改
三、defineEmits:向父组件发送消息
1. 作用
子组件触发事件,父组件监听并响应(类似Vue2的$emit
)
2. 快速上手
vue
<!-- 子组件 Child.vue -->
<template>
<button @click="sendMsg">发送消息</button>
</template>
<script setup>
// 声明事件
const emits = defineEmits(['message'])
function sendMsg() {
emits('message', '来自子组件的问候') // 触发事件并传递数据
}
</script>
vue
<!-- 父组件 Parent.vue -->
<template>
<Child @message="handleMessage" />
</template>
<script setup>
function handleMessage(msg) {
alert(msg) // 弹出:来自子组件的问候
}
</script>
3. 两个重要注意事项
-
事件命名 :建议使用kebab-case(如
update-count
) -
参数传递 :可传递多个参数
vueemits('event-name', arg1, arg2)
四、defineExpose:暴露子组件能力
1. 作用
允许父组件直接调用子组件的方法或访问属性
2. 快速上手
vue
<!-- 子组件 Child.vue -->
<template>
<div ref="boxRef"></div>
</template>
<script setup>
import { ref } from 'vue'
const boxRef = ref(null)
// 暴露方法
function focusInput() {
boxRef.value.focus()
}
// 显式暴露
defineExpose({
focusInput
})
</script>
vue
<!-- 父组件 Parent.vue -->
<template>
<Child ref="childRef" />
<button @click="callChildMethod">调用子组件方法</button>
</template>
<script setup>
import { ref } from 'vue'
const childRef = ref()
function callChildMethod() {
childRef.value.focusInput() // 调用子组件方法
}
</script>
3. 使用场景
- 控制子组件内部状态(如弹窗显示/隐藏)
- 直接操作子组件DOM元素
- 调用子组件内部方法
五、综合示例:购物车组件
1. 父组件(ShoppingCart.vue)
vue
<template>
<ProductItem
:product="product"
@add-to-cart="handleAdd"
ref="productRef"
/>
<button @click="showDetails">显示商品详情</button>
</template>
<script setup>
import { ref } from 'vue'
import ProductItem from './ProductItem.vue'
const product = { id: 1, name: 'Vue3教程' }
const productRef = ref()
function handleAdd() {
console.log('加入购物车')
}
function showDetails() {
productRef.value.showDetails() // 调用子组件方法
}
</script>
2. 子组件(ProductItem.vue)
vue
<template>
<div>
<h3>{{ product.name }}</h3>
<button @click="emits('add-to-cart')">加入购物车</button>
</div>
</template>
<script setup>
const props = defineProps(['product'])
const emits = defineEmits(['add-to-cart'])
// 暴露方法
function showDetails() {
alert('显示商品详情')
}
defineExpose({
showDetails
})
</script>
六、总结:三步掌握组件通信
- 数据传递 :父组件用属性传递,子组件用
defineProps
接收 - 事件通信 :子组件用
defineEmits
触发,父组件用@
监听 - 方法调用 :子组件用
defineExpose
暴露,父组件用ref
调用
七、练习建议
- 创建一个父组件和两个子组件,实现:
- 父组件传递用户名到子组件A
- 子组件A发送消息到父组件
- 父组件调用子组件B的方法显示弹窗
- 尝试给props添加类型校验和默认值
- 实践事件传递多个参数
通过这三个API,你可以完成Vue3中90%的组件通信需求。当项目复杂度增加时,再考虑使用Vuex或Pinia进行状态管理。