【Vue3】provide和inject 依赖与注入
provide与inject 依赖注入
vue3中使用setup语法糖与vue2的provide与inject的区别还是挺大的,这里不讨论vue3的配置项写法。
vue2的可以参考学习之前的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>
总结
-
provide在vue3里的setup语法糖里只支持一次provide一个变量,这点是不如vue2的,vue2(配置项写法)可以批量使用。
-
直接使用provide和inject为非响应式,我们知道在vue2中使用provide想达到响应式可以在provide的时候用函数包裹住并在,inject的时候用computed调用这个函数拿到数据。在vue3中想做到响应式比较方便,直接使用ref或者reactive包裹住数据,即可做到依赖注入的响应式。
-
provide只支持在setup语法糖里调用,
-
禁止子孙组件修改祖先组件的注入,可以在祖先组件使用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>
洗尽铅华始见金,褪去浮华归本真
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?