第3章:组件化开发基础
第8节:自定义事件与组件通信
3.8.1 自定义事件定义与触发
在Vue.js中,子组件可以通过自定义事件向父组件传递数据和通知。子组件使用$emit()方法触发事件,父组件通过v-on或@指令监听事件。
子组件触发自定义事件
<!-- 子组件 ChildComponent.vue -->
<template>
<button @click="$emit('enlarge-text', 0.1)">
放大文字
</button>
</template>在选项式API中,也可以在方法中触发事件:
<!-- 子组件 ChildComponent.vue -->
<template>
<button @click="handleClick">
放大文字
</button>
</template>
<script>
export default {
methods: {
handleClick() {
// 触发自定义事件,并传递参数
this.$emit('enlarge-text', 0.1)
}
}
}
</script>父组件监听自定义事件
<!-- 父组件 ParentComponent.vue -->
<template>
<div>
<p :style="{ fontSize: postFontSize + 'em' }">这是一段文字</p>
<ChildComponent @enlarge-text="postFontSize += $event" />
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue'
export default {
components: {
ChildComponent
},
data() {
return {
postFontSize: 1
}
}
}
</script>在组合式API中使用自定义事件
<!-- 子组件 ChildComponent.vue -->
<template>
<button @click="emit('enlarge-text', 0.1)">
放大文字
</button>
</template>
<script setup>
// 定义组件可以触发的事件
const emit = defineEmits(['enlarge-text'])
</script>3.8.2 事件验证与.emits选项
在Vue 3中,可以使用.emits选项对自定义事件进行验证和类型检查。
事件验证(选项式API)
export default {
// 声明组件可以触发的事件
emits: {
// 简单声明
'enlarge-text': null,
// 带验证的事件
'update:modelValue': (value) => {
// 验证函数,返回true表示验证通过
return typeof value === 'number' || typeof value === 'string'
}
},
methods: {
handleClick() {
this.$emit('enlarge-text', 0.1)
}
}
}在组合式API中验证事件
<script setup>
// 带验证的事件声明
const emit = defineEmits({
'enlarge-text': (value) => {
return typeof value === 'number' && value > 0
}
})
// 使用TypeScript类型
const emit = defineEmits<{
(e: 'enlarge-text', value: number): void
(e: 'close'): void
}>()
</script>3.8.3 v-model在组件上的使用
v-model是Vue的双向绑定指令,它可以在组件上使用,实现父子组件之间的数据双向同步。
基本用法
<!-- 父组件 -->
<template>
<CustomInput v-model="searchText" />
</template>
<!-- 等价于 -->
<template>
<CustomInput
:modelValue="searchText"
@update:modelValue="newValue => searchText = newValue"
/>
</template>子组件实现
<!-- 子组件 CustomInput.vue -->
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', ($event.target as HTMLInputElement).value)"
/>
</template>
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>使用自定义prop和事件名
可以通过v-model的参数来自定义prop和事件名:
<!-- 父组件 -->
<template>
<CustomInput v-model:search="searchText" />
</template>
<!-- 子组件 CustomInput.vue -->
<template>
<input
:value="search"
@input="$emit('update:search', ($event.target as HTMLInputElement).value)"
/>
</template>
<script setup>
const props = defineProps(['search'])
const emit = defineEmits(['update:search'])
</script>3.8.4 多个v-model绑定
在Vue 3中,一个组件可以支持多个v-model绑定:
<!-- 父组件 -->
<template>
<UserName
v-model:first-name="firstName"
v-model:last-name="lastName"
/>
</template>
<!-- 子组件 UserName.vue -->
<template>
<div>
<input
type="text"
:value="firstName"
@input="$emit('update:first-name', ($event.target as HTMLInputElement).value)"
/>
<input
type="text"
:value="lastName"
@input="$emit('update:last-name', ($event.target as HTMLInputElement).value)"
/>
</div>
</template>
<script setup>
const props = defineProps(['firstName', 'lastName'])
const emit = defineEmits(['update:first-name', 'update:last-name'])
</script>自定义事件最佳实践
- 使用语义化的事件名:事件名应该清晰反映事件的含义,如
update、close、submit等 - 遵循kebab-case命名:事件名使用kebab-case(短横线分隔),如
enlarge-text - 明确事件参数:为事件传递必要的参数,方便父组件处理
- 使用
.emits选项:声明组件可以触发的事件,提高代码的可维护性 - 避免过度使用事件:对于复杂的组件通信,可以考虑使用状态管理库
总结
自定义事件是Vue.js中实现子组件向父组件通信的重要方式。通过掌握自定义事件的定义、触发、监听和验证,以及v-model在组件上的使用,你可以实现灵活高效的组件间通信。
在下一节中,我们将学习组件间通信的另一种方式:插槽(Slots)系统。