父组件对子组件数据监听

166 阅读2分钟

一、基础方案: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,适合大型应用。

五、性能优化与注意事项

  1. 深度监听(deep: true)的性能开销

    • 避免对大型对象使用deep: true,可改用watch监听特定属性(如childData.name)。
  2. 组件销毁时取消监听

    • 对Event Bus监听的事件,需在beforeDestroy中通过$off取消绑定,避免内存泄漏。
  3. v-model与.sync的本质

    • 二者均是语法糖:v-model默认绑定valueinput事件,.sync绑定propupdate:prop事件,可根据业务场景选择。

六、总结:不同场景的方案选择

场景推荐方案优势
基础父子组件通信props + $emit灵活性高,符合单向数据流原则
表单类双向绑定v-model语法简洁,贴近原生表单操作
简化props更新.sync修饰符减少事件监听代码量
深层嵌套组件通信Provide/Inject避免多层props传递
复杂状态管理Vuex集中管理,适合大型应用