【Vue3】provide和inject 依赖与注入

provide与inject 依赖注入

vue3中使用setup语法糖与vue2的provide与inject的区别还是挺大的,这里不讨论vue3的配置项写法。

vue2的可以参考学习之前的vue2的provide与inject文章。

🚩
【Vue2】provide与inject 依赖注入

使用

祖先组件

<template>
    <div>祖先组件{{obj.lv1.lv2.num}}</div>
    <button @click="obj.lv1.lv2.num++">按钮++</button>
    <Child />
</template>

<script setup lang='ts'>
import Child from './Child.vue'
import { ref,provide,readonly } from 'vue';

interface T{
    name:string,
    age:number,
    lv1:{
        lv2:{
            num:number
        }
    }
}
// 为保证依赖注入是响应式,使用ref
let obj = ref<T>({
    name:'paul',
    age:18,
    lv1:{
        lv2:{
                num:666
        }
    }
})
// 看源码可知setup语法糖只支持一次provide一个变量(key为string或number或InjectionKey<T>(后面再讲))
// export declare function provide<T>(key: InjectionKey<T> | string | number, value: T): void;
// 子孙组件可以直接逆向修改祖先组件的传递过来的值,这是不允许的。所以可以增加一个readonly,防止子组件修改祖先组件造成混乱。
provide('obj',readonly(obj))
</script>

子组件

<template>
    <div>子组件{{obj?.lv1.lv2.num}}</div>
    <GrandChild />
</template>
<script setup lang='ts'>
import GrandChild from './GrandChild.vue'
import { inject } from 'vue';
interface T{
    name:string,
    age:number,
    lv1:{
        lv2:{
            num:string
        }
    }
}
let obj = inject<T>('obj')
</script>

子孙组件

<template>
    <div>子孙组件{{obj?.lv1.lv2.num}}</div>
</template>

<script setup lang='ts'>
import { inject } from 'vue';
interface T{
    name:string,
    age:number,
    lv1:{
        lv2:{
            num:number
        }
    }
}
let obj = inject<T>('obj')
</script>

总结

  1. provide在vue3里的setup语法糖里只支持一次provide一个变量,这点是不如vue2的,vue2(配置项写法)可以批量使用。

  2. 直接使用provide和inject为非响应式,我们知道在vue2中使用provide想达到响应式可以在provide的时候用函数包裹住并在,inject的时候用computed调用这个函数拿到数据。在vue3中想做到响应式比较方便,直接使用ref或者reactive包裹住数据,即可做到依赖注入的响应式。

  3. provide只支持在setup语法糖里调用,

  4. 禁止子孙组件修改祖先组件的注入,可以在祖先组件使用readonly将变量设置为只读。

如果你试验过上述代码会发现,根本就无法在子孙组件里安全的修改注入的值,因为ts根本读取不到inject值的安全类型,为什么呢?因为子组件拿到的值根本无法获取到类型。。。以下为踩坑,vue3在provide和inject上的设计很失败

由官网可知,通过引入InjectionKey继承Symbol的泛型类,并给inject赋予默认值,子孙组件有默认值。

祖先组件

<template>
    <div>祖先组件{{ obj.lv1.lv2.num }}</div>
    <button @click="obj.lv1.lv2.num++">按钮++</button>
    <Child />
</template>

<script setup lang='ts'>
import Child from './Child.vue'
import { ref, provide, readonly } from 'vue';
import {key} from '../Symbol'

interface T {
    name: string,
    age: number,
    lv1: {
        lv2: {
            num: number
        }
    }
}
// 为保证依赖注入是响应式,使用ref
let obj = ref<T>({
    name: 'paul',
    age: 18,
    lv1: {
        lv2: {
            num: 666
        }
    }
})
// 看源码可知setup语法糖只支持一次provide一个变量
// export declare function provide<T>(key: InjectionKey<T> | string | number, value: T): void;
// 子孙组件可以直接逆向修改祖先组件的传递过来的值,这是不允许的。所以可以增加一个readonly,防止子组件修改祖先组件造成混乱。
provide('obj', readonly(obj))

// 引入symbol文件管理key,以在子组件获得安全类型
provide(key, obj)
</script>

管理symbol的文件

// 管理Symbol 文件夹
import {InjectionKey,Ref} from 'vue'

interface T{
    name:string,
    age:number,
    lv1:{
        lv2:{
            num:string
        }
    }
}

export const key :InjectionKey<Ref<T>> = Symbol()

子孙组件

<template>
    <div>子孙组件{{obj.lv1.lv2.num}}</div>
    <button @click="clickEvent">修改祖先组件的值</button>
</template>

<script setup lang='ts'>
import {key} from '../Symbol'
import { inject } from 'vue';
interface T{
    name:string,
    age:number,
    lv1:{
        lv2:{
            num:number
        }
    }
}

// 注入仍有可能为undefined,则需要准备好默认值
let defaultInjectValue = <T>{
    name:'tom',
    age:999,
    lv1:{
        lv2:{
            num:999
        }
    }
}

// 由于定义好了InjectionKey,这里定义泛型,才能让值接收到。。。感觉像在开手动挡车一样繁琐。
let obj = inject<T>(key,defaultInjectValue)
// 反向修改祖先的值
const clickEvent = () => {
    obj.lv1.lv2.num =6
}
</script>
posted @ 2022-10-28 23:46  wanglei1900  阅读(1105)  评论(0编辑  收藏  举报