是一种非常常见的跨组件传递数据的方法,它可以帮助我们更好地管理组件之间的依赖关系。本文将介绍Vue3中的依赖注入机制,包括provide()和inject()函数的使用方法、使用注意以及优缺点和适用场景等方面的内容。✨快速上手Vue3中的依赖注入机制提供provide()...](/templets/3_zixun/picture/199.jpg)
在中,依赖注入[1](DI)是一种非常常见的跨组件传递数据的方法,它可以帮助我们更好地管理组件之间的依赖关系。本文将介绍Vue3中的依赖注入机制,包括provide()和inject()函数的使用方法......

在中,依赖注入[1](DI)是一种非常常见的跨组件传递数据的方法,它可以帮助我们更好地管理组件之间的依赖关系。本文将介绍Vue3中的依赖注入机制,包括provide()和inject()函数的使用方法、使用注意以及优缺点和适用场景等方面的内容。
✨快速上手Vue3中的依赖注入机制提供provide()和inject()函数,用于实现「组件之间的依赖关系传递和共享」。
介绍在没有依赖注入机制之前,开发者经常会遇到「组件属性逐级透传」的问题,也就是「组件的属性需要逐层往深层子组件进行传递」,导致链路很长,非常麻烦。

为了解决这个问题,Vue3提供的依赖注入机制,只需要在父组件提供(provide)依赖,任何层级的后代组件注入该依赖即可。

下面先介绍provide()和inject()这两个函数的接口定义:
provide(name,value)
通常在父组件使用,提供一个值,可以被任意层级子组件注入。
functionprovideT(key:InjectionKeyT|string,value:T):void;
该函数接收2个参数,参数name为「注入的key」,可以是「字符串」或者Symbol,子组件通过该值来注入,参数value为需要注入的依赖值,可以是任何类型的值。
inject
常在子组件使用,注入一个由父组件或整个应用(通过())提供的值。
//没有默认值functioninjectT(key:InjectionKeyT|string):T|undefined;//带有默认值functioninjectT(key:InjectionKeyT|string,defaultValue:T):T;//使用工厂函数functioninjectT(key:InjectionKeyT|string,defaultValue:()=T,treatDefaultAsFactory:true):T;
该函数接收2个参数,参数key为父组件提供值的**key**,参数defaultValue为可选参数,作为依赖的默认值,可以是具体的值,也可以是函数,来创建复杂的值,参数treatDefaultAsFactory也是可选值,当defaultValue为函数时,需要设置treatDefaultAsFactory为false,表明这个函数是默认值,而不是工厂函数。
使用示例下面是provide()和inject()函数的使用示例:
!--父组件--templatechild-component/child-component/templatescriptsetuplang="ts"import{provide}from"vue";provide("name","Chris");/script!--子组件--templatedivname:{{name}}/div/templatescriptsetuplang="ts"import{inject}from"vue";constname=inject("name","defaultName");/script在上面的示例中,我们在父组件中使用provide('name','Chris')提供了一个注入名为name的值,值为'Chris'。在子组件中使用inject('name','defaultName')注入这个值,并赋值给变量name,添加到模版中。
使用场景通常有以下使用常见:
「大型项目」:在大型项目中,组件之间的依赖关系比较复杂,使用依赖注入可以更好地管理这些依赖关系。
「可重用性要求高的项目」:在需要重用代码的项目中,使用依赖注入可以提高代码的可重用性。
「需要进行单元测试的项目」:在需要进行单元测试的项目中,使用依赖注入可以使测试更容易进行。
❓常见问题使用provide()和inject()时需要注意以下问题:
inject()只能使用在setup()或函数组件中如果没有使用scriptsetup,inject()需要在setup()内同步调用:比如:
scriptsetuplang="ts"import{provide}from"vue";provide("name","Chris");/script或者:
import{inject}from'vue'exportdefault{setup(){constmessage=inject('message')return{message}}}provide()注入名可以为Symbol类型使用provide(name,value),name参数可以支持不同类型的值,包括:
字符串:如provide('name','Chris');
Symbol:如provide(Symbol(),'Chris'),当我们在开发大型且依赖多的应用时,可以使用Symbol类型作为注入名,「避免冲突」;
接下来是使用Symbol+TypeScript的一个示例代码:
//{InjectionKey}from"vue";exportconstsymbolStringKey=Symbol()asInjectionKeystring;//父组件import{provide}from"vue";import{symbolStringKey}from"./key";provide(symbolStringKey,"Chris");//子组件import{inject}from"vue";import{symbolNumberKey}from"./key";constsymbolNumber=inject(symbolNumberKey);在使用TypeScript时,可以使用InjectionKey泛型类型,并使用注入值的类型作为泛型参数。
provide()注入值为响应式数据使用provide(name,value),value参数可以支持不同类型的值,包括:
普通类型:如字符串,数字,普通对象等;
响应式类型:如Vue3的ref,reactive,readonly等,如果是响应式数据,则该值发生变化后,有注入该值的任何层级的子组件,都会更新这个值;
接下来演示一下响应式类型的示例:
父组件
scriptsetuplang="ts"import{provide,ref,reactive,readonly}from"vue";importChild1from"./";constuser={name:"Chris",age:18};constuserRef=ref({name:"Chris",age:18});constuserReactive=reactive({name:"Chris",age:18});constuserReadonly=readonly({name:"Chris",age:18});provide("name","Chris");provide("age",18);provide("user",user);provide("userRef",userRef);provide("userReactive",userReactive);provide("userReadonly",userReadonly);constchangeUser=()={="NewChris";=30;};constchangeUserRef=()={="RefChris";=30;};constchangeUserReactive=()={="ReactiveChris";=30;};constchangeUserReadonly=()={//@="ReadonlyChris";//@=30;};/scripttemplatedivclass="ProvideInject"divRootComponent/divbutton@click="changeUser"Updateuser/buttonbutton@click="changeUserRef"UpdateuserRef/buttonbutton@click="changeUserReactive"UpdateuserReactive/buttonbutton@click="changeUserReadonly"UpdateuserReadonly/buttondivuser:{{}}/{{}}/divdivuserRef:{{}}/{{}}/divdivuserReactive:{{}}/{{}}/divdivuserReadonly:{{}}/{{}}/divChild1/Child1/div/template子组件
scriptsetuplang="ts"import{inject,ref,reactive,readonly}from"vue";constname=inject("name","defaultName");constage=inject("age",20);constuser=inject("user",{name:"",age:22});constuserRef=inject("userRef",ref({name:"",age:22}));constuserReactive=inject("userReactive",reactive({name:"",age:22}));constuserReadonly=inject("userReadonly",readonly({name:"",age:22}));constchangeUserRef=()={="Child1RefChris";=30;};constchangeUserReactive=()={="Child1ReactiveChris";=30;};constchangeUserReadonly=()={//@="Child1ReadonlyChris";//@=30;};/scripttemplatedivclass="Child1"divChildComponent1/divbutton@click="changeUserRef"UpdateuserRef/buttonbutton@click="changeUserReactive"UpdateuserReactive/buttonbutton@click="changeUserReadonly"UpdateuserReadonly/buttondivname:{{name}}/divdivage:{{age}}/divdivuser:{{}}/{{}}/divdivuserRef:{{}}/{{}}/divdivuserReactive:{{}}/{{}}/divdivuserReadonly:{{}}/{{}}/div/div/template在这个示例中,父组件使用provide()函数提供普通对象、ref响应式对象、reactive响应式对象、readonly响应式对象,然后子组件分别注入这些依赖并将值展示在视图中。最后在父子组件分别提供按钮修改这些值,观察父子组件视图上数据的变化。可以观察到,普通对象变化后,子组件视图并不会更新,而如果是「响应式对象」发生变化,则「子组件视图更新」。
尽量在提供方组件更新响应式数据由于响应式数据作为provide()提供的值,可以在任意层级的子组件注入,并且修改后会响应式变化,这就导致很多时候,「我们无法知道是在哪个子组件修改了这个响应式数据」。因此建议开发者尽量在父组件,也就是响应式数据提供方的组件进行更新数据,确保提供状态声明和变更操作都在同一个组件,方便维护。
scriptsetuplang="ts"import{provide,ref,reactive,readonly}from"vue";importChild1from"./";constuserRef=ref({name:"Chris",age:18});provide("userRef",userRef);constchangeUserRef=()={="RefChris";=30;};/scripttemplatedivclass="ProvideInject"divRootComponent/divbutton@click="changeUserRef"UpdateuserRef/buttondivuserRef:{{}}/{{}}/divChild1/Child1/div/template在这个示例代码中,父组件通过provide()提供了userRef响应式数据,并且通过changeUserRef方法修改userRef的值。当子组件需要修改响应式数据时,可以在父组件也提供一个修改值的方法:
父组件
scriptsetuplang="ts"import{provide,ref,reactive,readonly}from"vue";importChild1from"./";constuserRef=ref({name:"Chris",age:18});provide("userRef",{userRef,changeUserRef});constchangeUserRef=()={="RefChris";=30;};/scripttemplatedivclass="ProvideInject"divRootComponent/divbutton@click="changeUserRef"UpdateuserRef/buttondivuserRef:{{}}/{{}}/divChild1/Child1/div/template子组件
scriptsetuplang="ts"import{inject}from"vue";const{userRef,changeUserRef}=inject("userRef");/scripttemplatedivclass="Child1"divChildComponent1/divbutton@click="changeUserRef"UpdateuserRef/buttondivuserRef:{{}}/{{}}/div/div/template上面示例代码中,父组件通过provide('userRef',{userRef,changeUserRef})将修改响应式数据的方法也提供出去,子组件注入依赖后,通过解构获取到changeUserRef方法,即可修改该响应式数据。
使用readonly()让注入方无法修改提供的数据如果开发者想让父组件提供的值数据,不能被子组件,也就是注入方修改,可以通过Vue3提供的readonly()方法来包装该值,接下来看个示例代码。
父组件
scriptsetuplang="ts"import{provide,readonly}from"vue";importChild1from"./";constuserReadonly=readonly({name:"Chris",age:18});provide("userReadonly",userReadonly);constchangeUserReadonly=()={//@="ReadonlyChris";//@=30;};/scripttemplatedivclass="ProvideInject"divRootComponent/divbutton@click="changeUserReadonly"UpdateuserReadonly/buttondivuserReadonly:{{}}/{{}}/divChild1/Child1/div/template子组件
scriptsetuplang="ts"import{inject,readonly}from"vue";constuserReadonly=inject("userReadonly",readonly({name:"",age:22}));constchangeUserReadonly=()={//@="Child1ReadonlyChris";//@=30;};/scripttemplatedivclass="Child1"divChildComponent1/divbutton@click="changeUserReadonly"UpdateuserReadonly/buttondivuserReadonly:{{}}/{{}}/div/div/template这个示例代码中,父组件使用provide()提供的值是个readonly()包装的值,子组件在注入之后,无法修改。
在嵌套provide时,存在同名的key会如何?由于provide可以无限层级的使用,经常就会出现provide的key名称重复的情况,那么这时候inject注入的值会变成什么?我们看看下面这个示例代码:
父组件
scriptsetuplang="ts"provide("name","Chris");//省略其他/scripttemplateChild1/Child1/template子组件
scriptsetuplang="ts"provide('name','ChildProvide')//省略其他/scripttemplateChild2/Child1/template孙组件
scriptsetuplang="ts"constname=inject("name","defaultName");//省略其他/scripttemplatedivname:{{name}}/div/template最后可以看到视图显示的是"name:ChildProvide"。所以当出现嵌套provide时,存在同名的key时,会优先使用最近的父组件的provide值。
优缺点优点「减少组件之间的耦合度」:依赖注入可以帮助我们更好地管理组件之间的依赖关系,减少组件之间的耦合度,使代码更容易维护和扩展。
「提高代码的可重用性」:依赖注入可以使代码更加模块化,提高代码的可重用性。
「更容易进行单元测试」:依赖注入可以使代码更容易进行单元测试,因为我们可以用mock对象替代实际对象,更方便地进行测试。
缺点「增加代码的复杂度」:依赖注入需要增加一些额外的代码来实现,这会增加代码的复杂度。
「可能会导致性能问题」:依赖注入可能会导致性能问题,因为它需要在运行时动态获取依赖关系。
总结本文主要介绍了Vue3中的依赖注入机制,包括provide()和inject()函数的使用方法、使用注意以及优缺点和适用场景等方面的内容。通过本文的介绍,相信读者可以更好地理解Vue3中的依赖注入机制,并在实际项目中进行应用。
拓展资料如果你想深入了解Vue3中的依赖注入机制,可以参考以下资料:
/Inject[6]
:UnderstandingtheDepencyInjectionSystem[7]
ThenewProvideandInjectinVue3[8]
希望这些资料能够对你有所帮助!
参考资料[1]
依赖注入:
[2]
Wiki-依赖注入:
[3]
:
[4]
:
[5]
[6]
/Inject:
[7]
:UnderstandingtheDepencyInjectionSystem:
[8]
ThenewProvideandInjectinVue3: