在 Vue3 中,子组件与父组件之间的通信可以通过回调函数(也称为自定义事件)来实现。以下是几种常见的方式:
1. 使用 emit 触发自定义事件
这是 Vue 中最标准的方式,通过 defineEmits 定义事件,然后通过emit触发。
子组件 ChildComponent.vue:
<script setup>
// 定义组件可以触发的事件
const emit = defineEmits(['updateValue'])
const handleClick = () => {
// 触发事件并传递数据
emit('updateValue', 'new value from child')
}
</script>
<template>
<button @click="handleClick">Send to Parent</button>
</template>
父组件 ParentComponent.vue:
<script setup>
import ChildComponent from './ChildComponent.vue'
const handleUpdate = (newValue) => {
console.log('Received from child:', newValue)
// 在这里处理子组件传递的数据
}
</script>
<template>
<ChildComponent @updateValue="handleUpdate" />
</template>
2. 使用 props 传递回调函数
也可以通过 props 传递回调函数:
子组件 ChildComponent.vue:
<script setup>
const props = defineProps({
callback: Function
})
const handleClick = () => {
props.callback('data from child')
}
</script>
<template>
<button @click="handleClick">Send to Parent</button>
</template>
父组件 ParentComponent.vue:
<script setup>
import ChildComponent from './ChildComponent.vue'
const handleChildData = (data) => {
console.log('Received:', data)
}
</script>
<template>
<ChildComponent :callback="handleChildData" />
</template>
3. 使用 v-model 实现双向绑定
对于表单元素等需要双向绑定的场景,可以使用 v-model :
子组件 InputComponent.vue:
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
const updateValue = (e) => {
emit('update:modelValue', e.target.value)
}
</script>
<template>
<input
:value="modelValue"
@input="updateValue"
/>
</template>
父组件 ParentComponent.vue:
<script setup>
import { ref } from 'vue'
import InputComponent from './InputComponent.vue'
const inputValue = ref('')
</script>
<template>
<InputComponent v-model="inputValue" />
<p>Value: {{ inputValue }}</p>
</template>
4. 使用 expose 暴露子组件方法
如果需要从父组件直接调用子组件的方法:
子组件 ChildComponent.vue:
<script setup>
const childMethod = () => {
console.log('Child method called')
return 'some data'
}
// 暴露方法给父组件
defineExpose({
childMethod
})
</script>
父组件 ParentComponent.vue:
<script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'
const childRef = ref(null)
const callChildMethod = () => {
if (childRef.value) {
const result = childRef.value.childMethod()
console.log('Result:', result)
}
}
</script>
<template>
<ChildComponent ref="childRef" />
<button @click="callChildMethod">Call Child Method</button>
</template>
实践建议
-
优先使用自定义事件 (emit) - 这是 Vue 推荐的方式,更符合单向数据流原则
-
避免过度使用 props 传递回调 - 这会使数据流难以追踪
-
对于表单元素使用 v-model - 提供更简洁的双向绑定语法
-
谨慎使用 expose - 仅在确实需要直接访问子组件方法时使用