一、基础方案:props + 自定义事件(最常用)
1. 子组件通过$emit主动通知父组件
- 子组件(ChildComponent.vue):
<script> export default { data() { return { childData: '初始值' } }, methods: { updateData(newValue) { this.childData = newValue; this.$emit('data-updated', newValue); // 触发自定义事件 } } } </script> - 父组件(ParentComponent.vue):
<template> <ChildComponent @data-updated="handleDataUpdate" /> </template> <script> export default { methods: { handleDataUpdate(value) { console.log('子组件数据更新:', value); // 父组件执行相应逻辑 } } } </script>
核心逻辑:子组件数据变化时主动通知父组件,父组件通过监听自定义事件响应。
二、进阶方案:.sync修饰符与v-model
1. .sync修饰符简化双向绑定
- 子组件(需定义update:propName事件):
<script> export default { props: { parentData: { type: String, default: '' } }, methods: { changeData() { this.$emit('update:parentData', '新值'); // 触发更新事件 } } } </script> - 父组件(使用.sync修饰符):
<ChildComponent :parentData.sync="parentState" /> <!-- 等价于 --> <ChildComponent :parentData="parentState" @update:parentData="parentState = $event" />
2. v-model实现双向绑定(适用于表单组件)
- 子组件(需定义value prop和input事件):
<script> export default { props: { value: { type: String, default: '' } }, methods: { handleInput(e) { this.$emit('input', e.target.value); // 触发input事件 } } } </script> - 父组件(使用v-model):
<ChildComponent v-model="parentValue" /> <!-- 等价于 --> <ChildComponent :value="parentValue" @input="parentValue = $event" />
三、高级方案:watch监听与依赖注入
1. 父组件直接watch子组件props
- 前提:子组件数据通过props传递给父组件。
- 父组件(在watch中监听子组件的props):
<template> <ChildComponent ref="child" :child-data="childData" /> </template> <script> export default { data() { return { childData: {} } }, watch: { // 监听childData的变化(深度监听对象属性) childData: { handler(newVal, oldVal) { console.log('子组件数据变化:', newVal); }, deep: true // 深度监听对象内部属性 }, // 或通过ref直接监听子组件实例属性 '$refs.child.childData'(newVal) { console.log('子组件实例数据变化:', newVal); } } } </script>
2. Provide/Inject依赖注入(适用于嵌套组件)
- 父组件(通过provide暴露数据监听方法):
<script> export default { provide() { return { // 暴露监听函数 listenChildData: (data) => { console.log('子组件数据:', data); // 父组件逻辑 } }; } } </script> - 深层子组件(通过inject获取监听函数):
<script> export default { inject: ['listenChildData'], methods: { updateData() { this.listenChildData('更新后的值'); // 主动通知父组件 } } } </script>
四、特殊场景:跨组件通信方案
1. 事件总线(Event Bus)
- 适用于:非父子组件间的监听(如兄弟组件)。
- 实现:
// event-bus.js import Vue from 'vue'; export const EventBus = new Vue(); // 子组件触发事件 EventBus.$emit('child-data-change', data); // 父组件监听事件 EventBus.$on('child-data-change', (data) => { /* 处理逻辑 */ });
2. Vuex状态管理
- 核心思想:子组件修改Vuex中的状态,父组件通过计算属性或watch监听状态变化。
- 优势:避免多层组件传递props,适合大型应用。
五、性能优化与注意事项
-
深度监听(deep: true)的性能开销
- 避免对大型对象使用
deep: true,可改用watch监听特定属性(如childData.name)。
- 避免对大型对象使用
-
组件销毁时取消监听
- 对Event Bus监听的事件,需在
beforeDestroy中通过$off取消绑定,避免内存泄漏。
- 对Event Bus监听的事件,需在
-
v-model与.sync的本质
- 二者均是语法糖:
v-model默认绑定value和input事件,.sync绑定prop和update:prop事件,可根据业务场景选择。
- 二者均是语法糖:
六、总结:不同场景的方案选择
| 场景 | 推荐方案 | 优势 |
|---|---|---|
| 基础父子组件通信 | props + $emit | 灵活性高,符合单向数据流原则 |
| 表单类双向绑定 | v-model | 语法简洁,贴近原生表单操作 |
| 简化props更新 | .sync修饰符 | 减少事件监听代码量 |
| 深层嵌套组件通信 | Provide/Inject | 避免多层props传递 |
| 复杂状态管理 | Vuex | 集中管理,适合大型应用 |