provide 意为「提供」,inject 意为「注入」,它们主要通过 provide 在组件内部提供一个子组件能够访问的数据,然后子组件通过 inject 在内部注入数据。
我们都知道我们在开发 Vue 项目的时候,都是以视图为线索的,把视图分割为无数的碎片(也就是组件,下文都会说是碎片)。然后把无数的碎片组合成我们想要的样子和功能。
碎片和碎片之间相互嵌套就会产生依赖关系,最终组合成一个页面交互的整体,这个整体就是组件树。
被嵌套的组件数据来源可能源于父组件,这样组件就需要通过 prop 进行定义配置。
原则上,组件是可以无限制的被依赖,数据也可以无限制的被传递下去,这就是形成了正常的单向数据流。
但是无限制的嵌套关系就会导致数据将穿过所有依赖关系中的组件,也就造成了许多层组件并未使用的数据出现。如果想要解决这个问题,就应该尽量让组件的依赖关系变得简单,组件之间的嵌套关系不能太深,组件化设计的时候就要考虑到组件的扁平化!!!
但是某些复杂的项目,我们又不得不嵌套的非常深,这个时候 provide/inject 就登场啦!
简单说就是父组件通过 provide 提供数据,子组件通过 inject 注入数据。
但是 provide/inject 又存在着很大的弊端:
1、父组件 provide 一个数据,无论哪个层级的子组件通过 inject 注入数据,这个数据都不是响应式的;
2、父组件不知道哪个子组件使用了 provide 的数据,子组件也不会知道谁提供了 inject 的数据;
使用案例
那么 provide/inject 如何做呢?
1、父组件 provide 提供一个数据
export default {provide: {message: 'hello!'}}
假如我们不想直接把message的内容定义好,而是要获取data中的某个属性,那么provide就不能是一个对象!!!而是改用函数的方式,这是因为data的数据是可变的,因为对象赋值是通过引用赋值的,当data中的数据发送变化时provide也会跟着发送变化,这样就会影响子组件的依赖数据!
export default {data() {return {message: 'hello!'}},provide: {// ❌❌❌// 这将会导致报错,因为无法获取到 thismessage: this.message}}
export default {data() {return {message: 'hello!'}},provide() {// ✅✅✅// 使用函数的形式,可以访问到 `this`return {message: this.message}}}
2、子组件通过 inject 来注入父组件提供的数据
export default {inject: ['message'],created() {console.log(this.message) // "hello"}}
而这里的inject数据时可以直接在data中拿到的,例如:
export default {inject: ['message'],data() {return {// 基于注入值的初始数据fullMessage: this.message}}}
默认情况下,inject注入名会被某个父组件提供。但如果该注入名的确没有任何组件提供,则会抛出一个运行时警告。
如果在注入一个值时不要求必须有提供者,那么我们应该声明一个默认值,和props类似:
export default {// 当声明注入的默认值时// 必须使用对象形式inject: {message: {from: 'message', // 当与原注入名同名时,这个属性是可选的default: 'default value'},user: {// 对于非基础类型数据,如果创建开销比较大,或是需要确保每个组件实例// 需要独立数据的,请使用工厂函数default: () => ({ name: 'John' })}}}
配合响应式
上面我们说了,provide提供的数据默认是不会响应式的,如果你想要实现响应式,你需要使用computed()函数提供一个计算属性:
export default {data() {return {message: 'hello!'}},provide() {return {// 显式提供一个计算属性message: Vue.computed(() => this.message)}}}
import { computed } from 'vue'export default {data() {return {message: 'hello!'}},provide() {return {// 显式提供一个计算属性message: computed(() => this.message)}}}
