vue2 依赖注入(Provide/Inject) + watch

用于跨层级组件通信(避免 props 层层传递),适用于深层嵌套的组件。
 

1. 父组件提供数据(Provide)

<script>
export default {
  provide() {
    return {
      appName: '我的应用', // 提供静态数据
      user: this.currentUser // 提供响应式数据(需用函数包裹确保响应式)
    };
  },
  data() {
    return { currentUser: { name: '管理员' } };
  }
};
</script>

2. 深层子组件注入数据(Inject)

<script>
export default {
  inject: ['appName', 'user'], // 注入父组件提供的数据
  mounted() {
    console.log(this.appName); // 输出:我的应用
    console.log(this.user.name); // 输出:管理员
  }
};
</script>

 

注意:

  • 注入的数据默认不是响应式的,若需响应式,应传递组件的响应式数据(如 this.currentUser);
  • 适用于插件开发或大型组件库,普通场景优先用 Vuex 或事件总线。

 

一、Provide/Inject 的响应式问题

Provide/Inject 本身不保证数据响应式,若提供的数据是非响应式的(如静态值),注入后无法监听变化。只有当提供的数据是组件的响应式数据(如 data 中的属性)时,注入后才能被监听
 
 

示例:基础用法(确保数据响应式)

// 父组件(提供数据)
<script>
export default {
  data() {
    return {
      userInfo: { name: "张三", age: 20 } // 响应式数据
    };
  },
  provide() {
    return {
      // 提供响应式数据(必须传递组件实例的响应式属性)
      user: this.userInfo
    };
  },
  methods: {
    updateUser() {
      this.userInfo.age = 21; // 修改响应式数据
    }
  }
};
</script>

二、在注入组件中用 watch 监听变化

注入数据后,可通过 watch 监听其变化,但需注意监听的 “深度”(针对对象 / 数组)。

1. 监听注入的对象(需开启 deep: true

// 深层子组件(注入并监听)
<script>
export default {
  inject: ["user"], // 注入父组件提供的 user
  watch: {
    // 监听注入的 user 对象(需深度监听)
    user: {
      handler(newVal, oldVal) {
        console.log("用户信息变化:", newVal);
      },
      deep: true, // 监听对象内部属性变化
      immediate: true // 初始化时立即执行一次
    },
    // 监听对象的某个具体属性(更高效)
    "user.age"(newVal) {
      console.log("年龄变化为:", newVal);
    }
  }
};
</script>

2. 监听注入的基本类型数据

若提供的是字符串、数字等基本类型,直接监听即可(无需 deep):
 
// 父组件提供基本类型
provide() {
  return { count: this.number }; // this.number 是 data 中的响应式数字
}

// 子组件监听
watch: {
  count(newVal) {
    console.log("数字变化:", newVal);
  }
}

三、常见问题与解决方案

1. 问题:注入的数据变化后,watch 不触发

原因:

 

    • 提供的不是响应式数据(如 provide() { return { user: { name: "张三" } } },这是普通对象,非响应式);
    • 直接替换了整个对象(破坏了响应式引用),例如:

 

// 错误:直接替换对象,响应式失效
this.userInfo = { name: "李四", age: 22 }; // 新对象会丢失响应式关联

 

 

解决方案:

 

    • 始终提供 data 中的响应式属性;
    • 修改对象时,更新属性而非替换整个对象
// 正确:修改属性(保持响应式引用)
this.userInfo.name = "李四";
this.userInfo.age = 22;

2. 问题:深层属性变化无法监听

解决方案:

 

    • 对对象开启 deep: true(但性能开销较大);
    • 精确监听具体属性(如 "user.age"),更高效。

 

3. 问题:需要在注入时立即获取初始值

解决方案:
在 watch 中添加 immediate: true,初始化时执行一次 handler
watch: {
  user: {
    handler(newVal) { /* 处理逻辑 */ },
    deep: true,
    immediate: true // 初始化立即执行
  }
}

四、完整示例:跨层级通信 + 监听

 

<!-- 爷爷组件(提供数据) -->
<template>
  <button @click="updateUser">修改用户信息</button>
  <parent-component />
</template>

<script>
import ParentComponent from './ParentComponent.vue';
export default {
  components: { ParentComponent },
  data() {
    return {
      user: { name: "初始姓名", age: 18 } // 响应式数据
    };
  },
  provide() {
    return { globalUser: this.user }; // 提供响应式数据
  },
  methods: {
    updateUser() {
      this.user.age += 1; // 修改属性(保持响应式)
    }
  }
};
</script>

<!-- 父亲组件(中间层,无需处理数据) -->
<template>
  <child-component />
</template>

<script>
import ChildComponent from './ChildComponent.vue';
export default { components: { ChildComponent } };
</script>

<!-- 孙子组件(注入并监听) -->
<template>
  <div>
    <p>姓名:{{ globalUser.name }}</p>
    <p>年龄:{{ globalUser.age }}</p>
  </div>
</template>

<script>
export default {
  inject: ["globalUser"],
  watch: {
    // 监听年龄变化
    "globalUser.age"(newVal) {
      console.log("年龄更新为:", newVal);
      alert(`年龄变为${newVal}岁`);
    },
    // 深度监听整个对象
    globalUser: {
      handler(newVal) {
        console.log("用户信息整体变化:", newVal);
      },
      deep: true
    }
  }
};
</script>

 

 

总结

  • Provide/Inject 用于跨层级传递数据,但需确保提供的是响应式数据(data 中的属性);
  • 注入后,可通过 watch 监听变化,对象需开启 deep: true,基本类型直接监听;
  • 避免直接替换对象,应修改属性以保持响应式关联。

 

 

 

 

 

 

 

 

 

 

 

 

 

posted on 2025-09-15 17:01  Mc525  阅读(262)  评论(0)    收藏  举报

导航