Provide/Inject - 我在Vue中很少使用到的一种父子通讯方式

最后更新:

也算是经典面试题的一部分了,对于父子间的通讯很多时候的使用我都是限于 props/$emit 来处理,或者 Vuex/EventBus 这种方式,很少会用到 Provide/Inject 来处理。其实这是一个很实用的跨级组件间通讯的方式。

这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。

看文档中关于这对API的解释就可以看到,向其所有后代 注入一个依赖,所以在跨级组件间通讯,或者 单父多子组件间通讯就会很方便了。

简单的使用可以查看官方文档中的示例,我就不举例了,因为使用起来真的很简单。

最直白的(但是错误的)可以理解为 props 的强化版本,暴露一个可以无视子组件的嵌套层级属性来进行注入。

在使用的过程中主要会遇到的问题基本上是以下两种:

#1 注入一个方法给后代组件使用:

注入一个方法给后代组件使用,只需要在 provide 返回的对象中声明想要暴露的方法即可。

// 父级组件提供 'fn'
export default {
  // 如果想要提供当前组件中的一些属性和方法,需要使用 return 一个对象来指定 this 的指向.
  // 不然会提示 `Cannot read properties of undefined` 或者 `xxx is not defined`。
  provide() {
    return {
      fn: this.customFn
    }
  },
  methods:{
    customFn(){
      // ....
    }
  }
  // ...
}
// 子组件注入 'fn'
export default {
  inject: ['fn'],
  // ...
}

#2 注入一个可响应的对象给后代组件使用:

其实也不复杂,查看官网的提示就可以知道应该如何操作了。

提示:provideinject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。

注入一个被监听的对象即可,比如说在 data 或者 computed 中声明的 对象对象数组

// 父级组件提供被监听的对象
export default {
  provide() {
    return {
      testObj: this.testObj,
      testObjArray: this.testObjArray
    }
  },
  data(){
    return {
      // 对象
      testObj:{ a:'aa', b:'bb' },
      // 对象数组
      testObjArray: [{ c:'ccc' }, { d:'ddd' }]
    }
  },
  mounted(){
    // 子组件初始化后修改变量值
    this.$nextTick(()=>{
      this.changeData()
    })
  },
  methods:{
    // 修改变量值
    changeData(){
      this.testObj.a = 'Lorem Ipsum'
      this.$set(this.testObjArray, 0, { c: 'new Value'})
    }
  }
}
// 子组件注入并监听
export default {
  inject: ['testObj','testObjArray'],
  watch:{
    testObj:{
      handler(val){
        console.log('child watcher: testObj changed', val);
      },
      deep:true
    },
    testObjArray(val){
      console.log('child watcher: testObjArray changed', val);
    }
  }
}

😣 遇到问题

#1 有些人可能会问为啥我修改了被监听的对象,但是没有触发子组件的监听事件?
一看问出这个问题的就是没有好好读文档,建议重新读一遍关于 watch文档
其实通过 deep 或者传入回调数组监听某一个用到的属性值即就行了,我上方的示例中也写了 watch 监听对象的方式,多仔细看看好吧。


文档

provide/inject - API — Vue.js 2.x
Provide/Inject - 组合式 API | Vue.js 3.x