Provide/Inject

132 阅读2分钟

Provide/Inject

Provide/Inject是跨组件通信的一种解决方案。这对组合不同于props,父组件provide提供的数据,不管是多么深层级的子组件都可以Inject注入使用。

用法

假设有这么三个组件:Father.vue、Son.vue、GrandSon.vue。

这三个组件分别代表:父组件、子组件、孙子组件。

我现在要将父组件(Father.vue)的数据,传给孙子组件(GrandSon.vue)使用。 反过来就是孙子组件内(Father)要使用父组件(Father.vue)的数据。

如果使用props的话,我们需要一级一级往下传,非常的麻烦。

使用Provide/Inject的话就不需要这样,父组件提供数据,后代组件直接注入使用即可。

  • provideObject | () => Object
  • injectArray<string> | { [key: string]: string | Symbol | Object }

根据官方的提示,project可以是一个对象也可以是一个返回值为对象的函数,推荐使用返回值为对象的函数的形式,inject可以是一个字符串数组,也可以是一个对象。

示例:

Father.vue

<template>
  <div class="dt-father">
    <Son />
  </div>
</template>

<script>
  export default {
      name: 'Father',
      data() {
          return {
              message: 'dt-father'
          }
      },
      // 提供给子组件的数据
      provide () {
          return {
              fatherMessage: this.message
          }
      }
  }
</script>

Son.vue

<template>
  <div class="dt-Son">
    <GrandSon />
  </div>
</template>

<script>
  export default {
    name: 'Son'
  }
</script>

GrandSon.vue

<template>
  <div class="dt-grandson">
    {{ fatherMessage() }} <!-- 结果:dt-father -->
  </div>
</template>

<script>
  export default {
    name: 'GrandSon',
    inject: ['fatherMessage']
  }
</script>

从上面的示例可以看出,不用经过子组件,孙子组件就可以直接使用到父组件中的数据,这给深层子组件数据通信提供了大大的便利。

提供响应式的数据

vue官方说明:使用provide提供inject注入的数据不是响应式的。

也就是说数据更新了,但是视图不会跟着渲染更新。

那有什么办法让父组件提供的数据是响应式的呢?

官方也有提供说明,意思大概就是这样的:

如果父组件想要通过provide提供响应式的数据给子组件去使用,要么将该数据用一个方法返回,要么该数据封装在一个对象里,对象里的属性是响应式的。

将数据用一个方法返回

示例: Father.vue

<template>
  <div class="dt-father">
    <Son />
  </div>
</template>

<script>
  export default {
      name: 'Father',
      data() {
          return {
              message: 'dt-father'
          }
      },
      // 提供给子组件的数据
      provide () {
          return {
              fatherMessage: () => this.message
          }
      }
  }
</script>

GrandSon.vue

<template>
  <div class="dt-grandson">
    {{ fatherMessage() }} <!-- 结果:dt-father -->
  </div>
</template>

<script>
  export default {
    name: 'GrandSon',
    inject: ['fatherMessage']
  }
</script>

### 将数据封装在一个对象里

示例:
`Father.vue`:
```html
<template>
  <div class="dt-father">
    <Son />
  </div>
</template>

<script>
  export default {
      name: 'Father',
      data() {
          return {
              message: 'dt-father'
          }
      },
      // 提供给子组件的数据
      provide () {
          return {
              fatherProvideData: {
                  fatherMessage: this.message
              }
          }
      }
  }
</script>

GrandSon.vue

<template>
  <div class="dt-grandson">
    {{ fatherMessage() }} <!-- 结果:dt-father -->
  </div>
</template>

<script>
  export default {
    name: 'GrandSon',
    inject: ['fatherMessage']
  }
</script>

修改inject注入的数据

子组件修改inject注入的数据,实际上是直接修改了provide提供数据的父组件中的数据。

引用类型例如数组、对象等可以直接provide子组件即可修改。基本类型需要使用将数据用一个方法返回的方式。

我们可以将需要修改的值作为参数传给函数,上面也看到了,子组件使用inject注入进来的数据需要函数式调用的,所以我们可以将需要修改的值当做参数传进去,然后再在父组件中provide中的函数内做逻辑处理。

示例: Father.vue

<template>
  <div class="dt-father">
    <Son />
  </div>
</template>

<script>
  export default {
      name: 'Father',
      data() {
          return {
              message: 'dt-father'
          }
      },
      // 提供给子组件的数据
      provide () {
          return {
              fatherProvideData: {
                  fatherMessage: (grandSonData) => {
                      this.message = grandSonData || this.message
                      return this.message
                  }
              }
          }
      }
  }
</script>

GrandSon.vue

<template>
  <div class="dt-grandson">
    {{ fatherMessage() }} <!-- 结果:dt-father -->
    <button @click="changeFatherMessage"></button>
  </div>
</template>

<script>
  export default {
    name: 'GrandSon',
    inject: ['fatherMessage'],
    methods: {
        // 修改父组件provide的数据
        changeFatherMessage () {
            this.fatherMessage('dt-grandson')
        }
    }
  }
</script>