provide/inject

225 阅读2分钟

一、provide/inject是啥

  • provide/inject用于【祖组件】和【后代组件】之间的传值,有点像加强版的props
  • 在【祖组件】中定义provide传递值
  • 在【后代组件】中通过inject接收值
  • 一般在开发中比较少用,适用于组件的开发
  • 和props相比,不需要一层一层地在子组件标签上定义props;和$parent相比,组件跨级时获取【祖组件】中的值比较方便
  watch: {
    '$parent.$parent.count': {
      handler(val) {
        console.log(val)
      }
    }
  }
  • 如果【祖组件】和【父组件】都传递了provide并且都是count,那么【子组件】中取【父组件】的count,就近原则
  • 如果【祖组件】的provide传递了count,【父组件】的provide传递了count1,在【子组件】中可以将inject写成对象形式,通过from属性明确当前值取自哪个provide
  inject: {
    count: { from: 'count' }
  }

二、实现一个简易的祖孙组件之间的传值

祖组件(App.vue)

<template>
  <div id="app">
    <p>{{count}}</p>
    <el-button @click="handleAdd">+</el-button>
    <Parent />
  </div>
</template>
<script>
import Parent from './components/Parent'
export default {
  name: 'app',
  data() {
    return { count: 0 }
  },
  provide() {
    return { count: this.count }
  },
  methods: {
    handleAdd() {
      this.count = ++this.count
    }
  },
  components: { Parent }
}
</script>

父组件(Parent.vue)

<template>
  <div class="parent">
    父组件
    <p>{{count}}</p>
    <Child />
  </div>
</template>
<script>
import Child from './Child'
export default {
  inject: ['count'],
  components: { Child }
}
</script>

子组件(Child.vue)

<template>
  <div class="child">
    子组件
    <p>{{count}}</p>
  </div>
</template>
<script>
export default { inject: ['count'] }
</script>

此时,【祖组件】中的count已经传递到【后代组件】中了,并且可以正常渲染。

但是,更新【祖组件】中的count时,后代组件不能够动态的更新。

动图.gif

三、响应式传值

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

①修改provide

  provide() {
    return {
      count: () => this.count  // 这里不要用解构赋值 const { count } = this; return { count }
    }
  }

②后代组件获取值时,count是一个函数,加上()

    <!-- <p>{{count}}</p> -->
    <p>{{count()}}</p>

此时,便可以实现响应式传值

动图.gif

其他方式

另外,provide传递了一个对象时,也是响应式的,即改变祖组件时孙组件会一起改变。但是当孙组件改变对象的属性时,祖组件也会跟着变,破坏了单项数据流原则,这会造成数据变化不可控

当然了,在inject中取到对象时,在props/data/computed/watch/created中用另一个变量接收深复制后的对象,但祖组件再次改变对象中的属性时,孙组件中不再跟着变化了

四、provide/inject可以定义成对象的结构

provide以对象形式定义,这种方式拓展性没有函数形式好,如果没有响应式可以采取这种方式传递参数(不推荐)

  provide: {
    count: '100'
  }

inject以对象形式定义,可以对provide中的参数重命名和设置默认值,需要时可以采取这种方式接收参数(推荐)

  inject: {
    newCount: { from: 'count', default: 100 }
  }

from的值,要和父组件/祖组件中provide提供的对象名称对应

五、后代组件如何改变【祖组件】中的provide

【祖组件】的provide中传入addCount

  provide() {
    return {
      count: () => this.count
      addCount: () => (this.count = this.count + 1)
    }
  }

【后代组件】接收addCount

  inject: ['count', 'addCount']

并触发addCount

    <el-button @click="addCount()">addCount</el-button>

动图.gif