在Vue.js开发中,父子组件通信是核心场景之一。其中,监听子组件事件(通过v-on或其缩写@)和绑定子组件数据(通过v-model)是两种最常用的通信方式。前者用于响应子组件触发的自定义事件,后者则通过语法糖实现数据的双向绑定。本文将结合具体代码示例,深入解析这两种机制的实现原理、使用场景及最佳实践。
v-on指令用于监听DOM事件或子组件触发的自定义事件。在父组件模板中,通过@eventName="handler"的语法绑定事件处理器。子组件则通过this.$emit('eventName', payload)触发事件并传递数据。
<!-- 父组件 -->
<template>
<ChildComponent @custom-event="handleCustomEvent" />
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: { ChildComponent },
methods: {
handleCustomEvent(payload) {
console.log('收到子组件数据:', payload);
}
}
}
</script>
<!-- 子组件 -->
<template>
<button @click="notifyParent">通知父组件</button>
</template>
<script>
export default {
methods: {
notifyParent() {
this.$emit('custom-event', { message: '来自子组件的数据' });
}
}
}
</script>Vue提供了一系列事件修饰符,可简化事件处理逻辑:
.stop:阻止事件冒泡.prevent:阻止默认行为.once:事件仅触发一次.self:仅当事件从元素本身触发时调用回调<!-- 阻止按钮点击事件冒泡到父元素 -->
<button @click.stop="handleClick">Click Me</button>
<!-- 阻止表单提交的默认跳转行为 -->
<form @submit.prevent="handleSubmit">
<input type="submit" value="提交">
</form>
<!-- 事件仅触发一次 -->
<button @click.once="handleOnce">一次性按钮</button>通过动态事件名(Vue 2.6+)和显式参数传递,可实现更灵活的事件处理:
<!-- 动态事件名绑定 -->
<ChildComponent @[dynamicEvent]="handleDynamicEvent" />
<!-- 传递事件对象与自定义参数 -->
<button @click="handleParam('hello', $event)">带参点击</button>
<script>
export default {
data() {
return { dynamicEvent: 'click' };
},
methods: {
handleParam(msg, event) {
console.log(msg, event.target); // 输出: "hello" <button>
}
}
}
</script>对于非父子关系的组件,可通过事件总线(Event Bus)实现通信:
// event-bus.js
import Vue from 'vue';
export const EventBus = new Vue();
// 组件A(发送事件)
import { EventBus } from './event-bus';
EventBus.$emit('global-event', { data: '跨组件消息' });
// 组件B(接收事件)
import { EventBus } from './event-bus';
EventBus.$on('global-event', (payload) => {
console.log(payload.data); // 输出: "跨组件消息"
});注意:事件总线需在组件销毁前手动移除监听,避免内存泄漏:
beforeUnmount() {
EventBus.$off('global-event', this.handler);
}v-model是Vue提供的语法糖,本质上是value属性与input事件的组合。在表单组件中,它简化了数据同步逻辑:
<!-- 父组件 -->
<template>
<CustomInput v-model="message" />
<p>Message: {{ message }}</p>
</template>
<script>
import CustomInput from './CustomInput.vue';
export default {
components: { CustomInput },
data() {
return { message: 'Hello Vue!' };
}
}
</script>
<!-- 子组件 -->
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>
<script>
export default {
props: ['modelValue'] // Vue 3默认属性名
}
</script>Vue 3允许通过参数自定义绑定的属性名和事件名:
<!-- 父组件:使用v-model:title绑定 -->
<CustomComponent v-model:title="pageTitle" />
<!-- 子组件:触发update:title事件 -->
<template>
<input
:value="title"
@input="$emit('update:title', $event.target.value)"
/>
</template>
<script>
export default {
props: ['title']
}
</script>在Vue 2中,.sync修饰符提供了类似的双向绑定能力,但Vue 3已将其移除,推荐使用v-model参数替代:
<!-- Vue 2的.sync语法 -->
<ChildComponent :value.sync="parentData" />
<!-- 等效的Vue 3实现 -->
<ChildComponent v-model:value="parentData" />对于非表单组件,可通过手动触发事件实现双向绑定:
<!-- 父组件 -->
<template>
<CounterComponent :count="num" @update:count="num = $event" />
</template>
<script>
import CounterComponent from './CounterComponent.vue';
export default {
components: { CounterComponent },
data() {
return { num: 0 };
}
}
</script>
<!-- 子组件 -->
<template>
<button @click="increment">增加计数</button>
</template>
<script>
export default {
props: ['count'],
methods: {
increment() {
this.$emit('update:count', this.count + 1);
}
}
}
</script>form-submitted而非formSubmitted。click)作为自定义事件名,防止冲突。scroll、resize),使用防抖(debounce)或节流(throttle)优化性能。nextTick或Web Worker。为事件和属性添加类型定义可提升代码可维护性:
// 子组件
export default defineComponent({
props: {
modelValue: {
type: String as PropType<string>,
required: true
}
},
emits: {
'update:modelValue': (value: string) => true
}
});特性 | v-on(事件监听) | v-model(数据绑定) |
|---|---|---|
核心作用 | 响应子组件事件 | 实现父子组件数据双向绑定 |
底层实现 |
|
|
自定义能力 | 修饰符、动态事件名 | 自定义属性名和事件名 |
适用场景 | 组件通信、用户交互 | 表单输入、状态同步 |
在实际开发中,可根据需求选择合适的方式:
v-on监听自定义事件。v-model简化双向绑定。通过深入理解这些机制,开发者能够编写出更清晰、更易维护的Vue组件代码。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。