什么是依赖注入
个人的理解 :
依赖注入,是在 一颗 组件树中,由 【前代组件】 给 【后代组件】 提供 属性值的 一种方式 ;
这种方式 突破了 【父子组件】之间通过 props 的方式传值的限制,只要是 【前代组件】提供的 依赖,【后代组件】都可以执行注入使用;
我愿称之为 【跨代传值】。
依赖注入使用到的 关键方法:
provide()
: 提供依赖
inject()
: 注入依赖
依赖注入使用的注意事项
1、依赖注入的组件 ,必须是 一棵组件树的 前后代关系,兄弟组件不支持;
2、不建议后代组件中直接修改 注入的依赖的值,建议 前代组件中 将修改依赖的方法一并暴露出去;
3、依赖注入支持 全局的注入(这个较为特殊一些);
借用官网的一张图就是下面的样子:
依赖注入的使用方式
1、注入普通值 : 直接指定 key 和 value
2、注入响应式的值 : 注入的值是一个 响应式的对象
3、注入时提供默认值 :inject() 方法 第二个参数可以指定默认值,当没有提供依赖时 展示的时默认值
4、限制 提供的数据不能被 后代修改 : 使用 readonly 进行限制
5、同时提供响应式的修改方法
6、全局提供依赖 ; 可以在 app 中进行全局提供依赖,所有的组件都可以注入依赖
7、使用 Symbol() 的方式指定 key
8、给 依赖注入 提供 类型约束
下面对上述的清醒进行案例介绍:
1、注入普通值
provide(key,value)
: 提供依赖
const value = inject(key)
: 注入依赖
前代组件 : 提供依赖
<template>
<div class="basediv">
APP.vue 中的 msg : {{ msg }}
<br><br>
<!-- 引入子组件 -->
<ChildComponent />
</div>
</template>
<script setup lang="ts">
// 引入 provide 方法
import { ref,provide } from 'vue'
// 引入子组件
import ChildComponent from './ChildComponent.vue'
// 声明父组件的一个变量
const msg = ref('这是父组件的msg变量')
// 提供一个普通的依赖对象
provide('key1','key1 的值')
</script>
<style scoped>
.basediv{
width: 600px;
height: 400px;
border: 1px solid red;
}
</style>
后代组件 : 注入依赖
<template>
<!-- 子组件 -->
<div class="childdiv">
子组件 - msg : {{ msg }}
<br>
注入的普通的依赖值 :{{ key1Value }}
</div>
</template>
<script setup lang="ts">
// 引入 inject 方法
import { ref,inject } from 'vue'
// 声明一个变量
const msg = ref('这是子组件的msg变量')
// 注入依赖 - 普通的数据
const key1Value = inject('key1')
console.log('后代组件中接收到的 key1 的值 : ',key1Value)
</script>
<style scoped>
.childdiv{
width: 350px;
border: 1px solid green;
}
</style>
运行效果:
2、注入响应式的值
前代组件 : 提供依赖
<template>
<div class="basediv">
APP.vue 中的 msg : {{ msg }}
<br>
APP.vue 中的 abc : {{ abc }}
<br>
<br>
<!-- 引入子组件 -->
<ChildComponent />
</div>
</template>
<script setup lang="ts">
// 引入 provide 方法
import { ref,provide } from 'vue'
// 引入子组件
import ChildComponent from './ChildComponent.vue'
// 声明父组件的一个变量
const msg = ref('这是父组件的msg变量')
// 提供一个响应式的依赖对象
const abc = ref(1001)
provide('key2',abc)
</script>
<style scoped>
.basediv{
width: 600px;
height: 400px;
border: 1px solid red;
}
</style>
后代组件 : 注入依赖
<template>
<!-- 子组件 -->
<div class="childdiv">
子组件 - msg : {{ msg }}
<br>
注入的响应式依赖 :{{ key2Value }}
</div>
</template>
<script setup lang="ts">
// 引入 inject 方法
import { ref,inject} from 'vue'
// 声明一个变量
const msg = ref('这是子组件的msg变量')
// 注入依赖-响应式的依赖
const key2Value = inject('key2')
console.log('后代组件中接收到的 key2 的值 :',key2Value)
</script>
<style scoped>
.childdiv{
width: 350px;
border: 1px solid green;
}
</style>
运行效果:
3、注入依赖时写一个默认值
后代组件 在进行 依赖注入时,如果前代组件没有 提供依赖,则可以只用默认值进行代替
默认值 是inject()
方法的第二个参数
前代组件 :
不
提供依赖
<template>
<div class="basediv">
APP.vue 中的 msg : {{ msg }}
<br> <br>
<!-- 引入子组件 -->
<ChildComponent />
</div>
</template>
<script setup lang="ts">
// 引入 provide 方法
import { ref,provide } from 'vue'
// 引入子组件
import ChildComponent from './ChildComponent.vue'
// 声明父组件的一个变量
const msg = ref('这是父组件的msg变量')
</script>
<style scoped>
.basediv{
width: 600px;
height: 400px;
border: 1px solid red;
}
</style>
后代组件 : 注入依赖
<template>
<!-- 子组件 -->
<div class="childdiv">
子组件 - msg : {{ msg }}
<br>
注入的响应式依赖 :{{ key2Value }}
</div>
</template>
<script setup lang="ts">
// 引入 inject 方法
import { ref,inject,toRef } from 'vue'
// 声明一个变量
const msg = ref('这是子组件的msg变量')
// 注入依赖-响应式的依赖
const key2Value = inject('key2','key2的默认值')
console.log('后代组件中接收到的 key2 的值 :',key2Value)
</script>
<style scoped>
.childdiv{
width: 350px;
border: 1px solid green;
}
</style>
运行效果:
4、提供响应式的修改方法
前代组件 : 提供依赖
此时提供的依赖是 一个对象
的形式 :包含数据
和方法
<template>
<div class="basediv">
APP.vue 中的 msg : {{ msg }}
<br>
APP.vue 中的 abc : {{ abc }}
<br>
<!-- 引入子组件 -->
<ChildComponent />
</div>
</template>
<script setup lang="ts">
// 引入 provide 方法
import { ref,provide } from 'vue'
// 引入子组件
import ChildComponent from './ChildComponent.vue'
// 声明父组件的一个变量
const msg = ref('这是父组件的msg变量')
// 提供一个响应式的依赖对象
const abc = ref(1001)
// 提供修改的方法
const changeABC = ()=>{
console.log('App 中修改 abc 的值为 888');
abc.value = 888;
}
// 提供 依赖对象 和 方法
provide('key2',{abc,changeABC})
</script>
<style scoped>
.basediv{
width: 600px;
height: 400px;
border: 1px solid red;
}
</style>
后代组件 : 注入依赖
<template>
<!-- 子组件 -->
<div class="childdiv">
子组件 - msg : {{ msg }}
<br>
注入的响应式依赖 :{{ key2Value }}
<br>
<button @click="changeAbcFromHd">修改 abc 的值</button>
</div>
</template>
<script setup lang="ts">
// 引入 inject 方法
import { ref,inject} from 'vue'
// 声明一个变量
const msg = ref('这是子组件的msg变量')
// 注入依赖-响应式的依赖
const key2Value = inject('key2')
console.log('后代组件中接收到的 key2 :',key2Value)
// 点击按钮修改 依赖的值
const changeAbcFromHd = ()=>{
key2Value.changeABC();
}
</script>
<style scoped>
.childdiv{
width: 350px;
border: 1px solid green;
}
</style>
运行效果: 注意观察点击前后的 数据状态的变化
初始页面 | 点击按钮后 |
---|---|
5、readonly 限制修改
当想限制传递的依赖只能是只读时,需要使用
readonly
关键字进行限制;
注意 : 只能是针对 没有提供修改函数的依赖,如果该依赖提供了修改函数,通过修改函数触发的修改仍然是有效的。
前代组件 : 提供依赖 : 一个普通的,一个只读的
<template>
<div class="basediv">
APP.vue 中的 msg : {{ msg }}
<br>
APP.vue 中的 abc : {{ abc }}
<br>
APP.vue 中的 xyz : {{ xyz }}
<br>
<!-- 引入子组件 -->
<ChildComponent />
</div>
</template>
<script setup lang="ts">
// 引入 provide 方法
import { ref,provide,readonly } from 'vue'
// 引入子组件
import ChildComponent from './ChildComponent.vue'
// 声明父组件的一个变量
const msg = ref('这是父组件的msg变量')
// 提供一个普通的依赖对象
// provide('key1','key1 的值')
// 提供一个响应式的依赖对象
const abc = ref(1001)
const xyz = ref(2002)
// 提供 依赖对象
provide('key2',abc)
// 提供一个只读的依赖对象
provide('key3',readonly(xyz))
</script>
<style scoped>
.basediv{
width: 600px;
height: 400px;
border: 1px solid red;
}
</style>
后代组件 : 注入依赖
<template>
<!-- 子组件 -->
<div class="childdiv">
子组件 - msg : {{ msg }}
<br>
<!-- 注入的普通的依赖值 :{{ key1Value }} -->
<!-- <br> -->
注入的响应式依赖key2 :{{ key2Value }}
<br>
注入的响应式依赖key3 :{{ key3Value }}
<br>
</div>
</template>
<script setup lang="ts">
// 引入 inject 方法
import { ref,inject } from 'vue'
// 声明一个变量
const msg = ref('这是子组件的msg变量')
// 注入依赖-响应式的依赖
const key2Value = inject('key2')
console.log('后代组件中接收到的 key2 :',key2Value)
const key3Value = inject('key3')
console.log('后代组件中接收到的 key3 :',key2Value)
// 延迟10s 执行修改
console.log(new Date())
setTimeout(() => {
console.log(new Date(), '开始执行 依赖值的修改' )
key2Value.value = 666;
key3Value.value = 888;
}, 10000);
</script>
<style scoped>
.childdiv{
width: 350px;
border: 1px solid green;
}
</style>
运行效果:
6、全局提供依赖
main.js : 提供全局依赖
import { createApp } from 'vue'
// 根组件
import App from './App.vue'
// 创建应用实例
const app = createApp(App)
// 直接注入一个全局的 依赖
app.provide('globalKey1','全局的变量值1')
// 挂在应用,渲染页面,这一步应该是在最后执行的
app.mount("#app")
后代组件 : 注入依赖
<template>
</template>
<script setup lang="ts">
// 引入 inject 方法
import { inject } from 'vue'
const globalValue = inject('globalKey1')
console.log('组件中注入全局的变量 :',globalValue)
</script>
<style scoped>
</style>
运行效果:
7、Symbol() 的方式指定 key
之前我们在提供依赖的时候,都是直接用字符串作为依赖的key,
说实话,这样做 一点毛病没有。
但是,大家不都是讲求规范嘛,Symbol()
就提供了这种方式:
1、写一个专门用来声明 key 的文件;
2、前代组件 引入 key,提供依赖;
3、后代组件 引入 key ,注入依赖。
key 文件
// 声明 两个key
export const KEY1 = Symbol()
export const KEY2 = Symbol()
前代组件 : 提供依赖
<template>
<div class="basediv">
APP.vue 中的 msg : {{ msg }}
<br>
APP.vue 中的 abc : {{ abc }}
<br>
<!-- 引入子组件 -->
<ChildComponent />
</div>
</template>
<script setup lang="ts">
// 引入 provide 方法
import { ref,provide } from 'vue'
// 引入子组件
import ChildComponent from './ChildComponent.vue'
// 声明父组件的一个变量
const msg = ref('这是父组件的msg变量')
// 引入定义的key
import { KEY1 ,KEY2} from './key';
// 提供依赖
const abc = ref(1001)
provide(KEY1,abc)
provide(KEY2,'key2valueladfadfa')
</script>
<style scoped>
.basediv{
width: 600px;
height: 400px;
border: 1px solid red;
}
</style>
后代组件 : 注入依赖
<template>
<!-- 子组件 -->
<div class="childdiv">
子组件 - msg : {{ msg }}
<br>
<!-- 注入的普通的依赖值 :{{ key1Value }} -->
<!-- <br> -->
注入的响应式依赖key :{{ keyValue }}
<br>
注入的响应式依赖key2 :{{ key2Value }}
<br>
</div>
</template>
<script setup lang="ts">
// 引入 inject 方法
import { ref,inject } from 'vue'
// 声明一个变量
const msg = ref('这是子组件的msg变量')
// 引入 key
import { KEY1,KEY2 } from './key';
// 注入依赖
const keyValue = inject(KEY1)
const key2Value = inject(KEY2)
</script>
<style scoped>
.childdiv{
width: 350px;
border: 1px solid green;
}
</style>
运行效果:
8、 依赖注入 提供 类型约束
InjectionKey
接口 : 限制 依赖注入的值的类型
key文件
// 从 vue 中 导入 类型
import type { Ref,InjectionKey } from 'vue'
// 声明 key : 指定注入的依赖的值的类型是 Ref
export const KEY1 = Symbol() as InjectionKey<Ref<number>>
// 声明 key : 指定注入的依赖的值的类型是 string
export const KEY2 = Symbol() as InjectionKey<string>
前代组件 : 提供依赖
<template>
<div class="basediv">
APP.vue 中的 msg : {{ msg }}
<br>
APP.vue 中的 abc : {{ abc }}
<br>
<!-- 引入子组件 -->
<ChildComponent />
</div>
</template>
<script setup lang="ts">
// 引入 provide 方法
import { ref,provide } from 'vue'
// 引入子组件
import ChildComponent from './ChildComponent.vue'
// 声明父组件的一个变量
const msg = ref('这是父组件的msg变量')
// 引入定义的key
import { KEY1 ,KEY2} from './key';
// 提供依赖
const abc = ref(1001)
// 提供一个响应式的对象依赖
provide(KEY1,abc)
// 提供一个 普通的 string 对象
provide(KEY2,'key2valueladfadfa')
</script>
<style scoped>
.basediv{
width: 600px;
height: 400px;
border: 1px solid red;
}
</style>
后代组件 : 注入依赖
<template>
<!-- 子组件 -->
<div class="childdiv">
子组件 - msg : {{ msg }}
<br>
<!-- 注入的普通的依赖值 :{{ key1Value }} -->
<!-- <br> -->
注入的响应式依赖key :{{ keyValue }}
<br>
注入的响应式依赖key2 :{{ key2Value }}
<br>
</div>
</template>
<script setup lang="ts">
// 引入 inject 方法
import { ref,inject } from 'vue'
// 引入 Ref 类型
import type { Ref } from 'vue';
// 声明一个变量
const msg = ref('这是子组件的msg变量')
// 引入 key
import { KEY1,KEY2 } from './key';
// 注入依赖 : 同样可以使用泛型指定类型
const keyValue = inject<Ref<number>>(KEY1)
const key2Value = inject<string>(KEY2,"默认值")
</script>
<style scoped>
.childdiv{
width: 350px;
border: 1px solid green;
}
</style>
运行效果:
至此,依赖注入的使用就完成了。