2022.11.19 学习Vue官方文档之依赖注入(provide/inject)

#前言

        首先我们由一个问题引入(假定读者了解了Vue父子组件通信的方法---prop/emit),我们已经知道了父子通信,那么祖孙通信、太爷和重孙的通信,我们该如何实现呢?哈哈很形象的比喻。

        就是说Vue组件可以是一个多层级嵌套的组件(<GrandFather />组件中使用了<Father />组件,而在father.Vue单文件组件中,又使用了<Son />组件,这就构成了组件树),我们现在需要实现<GrandFather />和<Son />之间的通信,其实我们可以用Props方法让prop从<GrandFather />逐级向下传递,但是这样会很麻烦,有时候<Father />组件根本不需要<GrandFather />传过来的属性,这样的花就给我自己添麻烦了。(祖孙间的悄悄话,为什么要让父亲知道呢?)

        这时候,Vue就提供给了我们依赖注入的方法(provide/inject)。一个父组件相对于其所有的后代组件,会作为依赖提供者(父组件提供给后代子组件需要的数据)。任何后代的组件树,无论层级有多深,都可以注入由父组件提供给整条链路的依赖。

 #Provide (提供)

要为组件后代提供数据,需要使用到 provide 选项。

        对于 provide 对象上的每一个属性,后代组件会用其 key 为注入名(给注入到后代组件中的数据起了个名字)查找期望注入的值,属性的值就是要提供的数据。

export default {
  provide: {
    message: 'hello!'
  }
}

        如果我们需要提供依赖当前组件实例的状态 (比如那些由 data() 定义的数据属性),那么可以以函数形式使用 provide。

export default {
  data() {
    return {
      message: 'hello!'
    }
  },
  provide() {
    // 使用函数的形式,可以访问到 `this`
    return {
      message: this.message
    }
  }
}

然而,请注意这不会使注入保持响应性!!!后续将会讨论如何将注入转变为响应式。

#应用层 Provide

除了在一个组件中提供依赖,我们还可以在整个应用层面提供依赖,这样我们就可以为任何一个组件注入我们在应用层面提供的依赖(数据)。

import { createApp } from 'vue'

const app = createApp({})

app.provide(/* 注入名 */ 'message', /* 值 */ 'hello!')

这在你编写插件特别有用,因为插件一般都不会使用组件形式来提供值。

#Inject (注入)

要注入上层组件提供的数据,需使用 inject 选项来声明

export default {
  inject: ['message'],
  created() {
    console.log(this.message) // injected value
  }
}

注入时机:注入会在组件自身的状态之前被解析,因此你可以在 data() 中访问到注入的属性

export default {
  inject: ['message'],
  data() {
    return {
      // 基于注入值的初始数据
      fullMessage: this.message
    }
  }
}

那么我们思考一个小问题吧。

        我们provide的时候,可以用key定义注入名(比如key为filmList),但是我们需要这个数据的子组件,自身的data()中已经有一个filmList的状态了,那我们就可以在注入的时候起别名了!

在接受方式上,我们不在向之前那样用字符串数组的形式接收,而采用了对象 { } 形式。

export default {
  inject: {
    /* 本地属性名 */ list: {
      from: /* 注入来源名 */ 'filmList'
    }
  }
}

这里,组件本地化了原注入名 "filmList" 所提供的属性,并将其暴露为 this.list。

#注入默认值

默认情况下,inject 假设传入的注入名会被某个祖先链上的组件提供。如果该注入名的确没有任何组件提供,则会抛出一个运行时警告。

如果在注入一个值时不要求必须有提供者,那么我们应该声明一个默认值,和 props 类似:

export default {
  // 当声明注入的默认值时
  // 必须使用对象形式
  inject: {
    message: {
      from: 'message', // 当与原注入名同名时,这个属性是可选的
      default: 'default value'
    },
    user: {
      // 对于非基础类型数据,如果创建开销比较大,或是需要确保每个组件实例
      // 需要独立数据的,请使用工厂函数
      default: () => ({ name: 'John' })
    }
  }
}

#和响应式数据配合使用

        前面我们所学的 provide 和 inject 都不具备响应性,为保证注入方和供给方之间的响应性链接,我们需要使用 computed() 函数提供一个计算属性。

import { computed } from 'vue'

export default {
  data() {
    return {
      message: 'hello!'
    }
  },
  provide() {
    return {
      // 显式提供一个计算属性
      message: computed(() => this.message)
    }
  }
}

!!!临时配置要求!!!

上面的用例需要设置 app.config.unwrapInjectedRef = true 以保证注入会自动解包这个计算属性。这将会在 Vue 3.3 后成为一个默认行为,而我们暂时在此告知此项配置以避免后续升级对代码的破坏性。在 3.3 后就不需要这样做了。

#使用 Symbol (独一无二的值)作注入名

        至此,我们已经了解了如何使用字符串作为注入名。但如果你正在构建大型的应用,包含非常多的依赖提供,或者你正在编写提供给其他开发者使用的组件库,建议最好使用 Symbol (ES6语法)来作为注入名以避免潜在的冲突。

// keys.js
export const myInjectionKey = Symbol()
// 在供给方组件中
import { myInjectionKey } from './keys.js'

export default {
  provide() {
    return {
      [myInjectionKey]: {
        /* 要提供的数据 */
      }
    }
  }
}
// 注入方组件
import { myInjectionKey } from './keys.js'

export default {
  inject: {
    localName: { from: myInjectionKey }
  }
}

为了更美好的明天而战!!!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值