Angular依赖注入体系中的基本概念是什么
Angular依赖注入体系中的基本概念是什么
这篇文章主要介绍“Angular依赖注入体系中的基本概念是什么”,在日常操作中,相信很多人在Angular依赖注入体系中的基本概念是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Angular依赖注入体系中的基本概念是什么”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
依赖注入
既然要介绍 Angular 框架的依赖注入设计,那么先铺垫一下依赖注入的基本概念。我们常常会搞混依赖倒置原则(DIP)、控制反转(IoC)、依赖注入(DI)这几个概念,因此这里会先简单介绍一下。
依赖倒置原则、控制反转、依赖注入
低耦合、高内聚大概是每个系统的设计目标之一,而为此产生了很多的设计模式和理念,其中便包括依赖倒置原则、控制反转的设计思想。
(1) 依赖倒置原则(DIP)。
依赖倒置原则的原始定义为:
高层模块不应该依赖低层模块,两者都应该依赖其抽象;
抽象不应该依赖细节,细节应该依赖抽象。
简单说便是:模块间不应该直接依赖对方,应该依赖一个抽象的规则(接口或者时抽象类)。
(2) 控制反转(IoC)。
控制反转的定义为:模块间的依赖关系从程序内部提到外部来实例化管理。即对象在被创建的时候,由一个调控系统内所有对象的外界实体控制,并将其所依赖的对象的引用传递(注入)给它。
实现控制反转主要有两种方式:
依赖注入:被动的接收依赖对象
依赖查找:主动索取依赖的对象
(3) 依赖注入。
依赖注入,是控制反转的最为常见的一种技术。
依赖倒置和控制反转两者相辅相成,常常可以一起使用,可有效地降低模块间的耦合。
Angular 中的依赖注入
在 Angular 中,同样使用了依赖注入的技术,DI 框架会在实例化某个类时,向其提供这个类所声明的依赖项(依赖项:指当类需要执行其功能时,所需要的服务或对象)。
Angular 中的依赖注入基本上是围绕着组件或者是模块展开的,主要用于给新建的组件提供依赖。
Angular 中主要的依赖注入机制是注入器机制:
应用中所需的任何依赖,都必须使用该应用的注入器来注册一个提供者,以便注入器可以使用这个提供者来创建新实例
Angular 会在启动过程中,创建全应用级注入器以及所需的其它注入器
这里面主要涉及两个概念,分别是Injector 注入器和Provider 提供商,我们来看看。
Injector 注入器
Injector 注入器用于创建依赖,会维护一个容器来管理这些依赖,并尽可能地复用它们。注入器会提供依赖的一个单例,并把这个单例对象注入到多个组件中。
显然,作为一个用来创建、管理、维护依赖的容器,注入器的功能很简单:创建依赖实例、获取依赖实例、管理依赖实例。我们也可以从抽象类Injector
的源码中看出来:
exportabstractclassInjector{//找不到依赖staticTHROW_IF_NOT_FOUND=THROW_IF_NOT_FOUND;//NullInjector是树的顶部//如果你在树中向上走了很远,以至于要在NullInjector中寻找服务,那么将收到错误消息,或者对于@Optional(),返回nullstaticNULL:Injector=newNullInjector();//根据提供的Token从Injector检索实例abstractget<T>(token:Type<T>|AbstractType<T>|InjectionToken<T>,notFoundValue?:T,flags?:InjectFlags):T;//创建一个新的Injector实例,该实例提供一个或多个依赖项staticcreate(options:{providers:StaticProvider[];parent?:Injector;name?:string;}):Injector;//ɵɵdefineInjectable用于构造一个InjectableDef//它定义DI系统将如何构造Token,并且在哪些Injector中可用staticɵprov=ɵɵdefineInjectable({token:Injector,providedIn:"any"asany,//ɵɵinject生成的指令:从当前活动的Injector注入Tokenfactory:()=>ɵɵinject(INJECTOR),});static__NG_ELEMENT_ID__=InjectorMarkers.Injector;}
也就是说,我们可以将需要共享的依赖实例添加到注入器中,并通过 Token 查询和检索注入器来获取相应的依赖实例。
需要注意的是,Angular 中的注入器是分层的,因此查找依赖的过程也是向上遍历注入器树的过程。
这是因为在 Angular 中,应用是以模块的方式组织的,具体可以参考5.模块化组织篇。一般来说,页面的 DOM 是以html
作为根节点的树状结构,以此为基础,Angular 应用中的组件和模块也是与之相伴的树状结构。
而注入器服务于组件和模块,同样是挂载与模块和组织上的树状结构。因此,Injector 也划分为模块和组件级别,可分别为组件和模块提供依赖的具体实例。注入器是可继承的,这意味着如果指定的注入器无法解析某个依赖,它就会请求父注入器来解析它,我们同样可以从上面的创建注入器代码中看到:
//创建一个新的Injector实例,可传入parent父注入器staticcreate(options:{providers:StaticProvider[],parent?:Injector,name?:string}):Injector;
在某个注入器的范围内,服务是单例的。也就是说,在指定的注入器中最多只有某个服务的最多一个实例。如果不希望在所有地方都使用该服务的同一个实例,则可以通过注册多个注入器、并按照需要关联到组件和模块中的方式,来按需共享某个服务依赖的实例。
我们可以看到创建一个新的Injector
实例时,传入的参数包括Provider
,这是因为Injector
不会直接创建依赖,而是通过Provider
来完成的。每个注入器会维护一个提供者的列表,并根据组件或其它服务的需要,用它们来提供服务的实例。
Provider 提供者
Provider 提供者用来告诉注入器应该如何获取或创建依赖,要想让注入器能够创建服务(或提供其它类型的依赖),必须使用某个提供者配置好注入器。
一个提供者对象定义了如何获取与 DI 令牌(token) 相关联的可注入依赖,而注入器会使用这个提供者来创建它所依赖的那些类的实例。
关于 DI 令牌:
当使用提供者配置注入器时,就会把提供者和一个 DI 令牌关联起来;
注入器维护一个内部令牌-提供者的映射表,当请求一个依赖项时就会引用它,令牌就是这个映射表的键。
提供者的类型很多,从官方文档中可以阅读它们的具体定义:
exporttypeProvider=|TypeProvider|ValueProvider|ClassProvider|ConstructorProvider|ExistingProvider|FactoryProvider|any[];
提供者的解析过程如下:
functionresolveReflectiveFactory(provider:NormalizedProvider):ResolvedReflectiveFactory{letfactoryFn:Function;letresolvedDeps:ReflectiveDependency[];if(provider.useClass){//使用类来提供依赖constuseClass=resolveForwardRef(provider.useClass);factoryFn=reflector.factory(useClass);resolvedDeps=_dependenciesFor(useClass);}elseif(provider.useExisting){//使用已有依赖factoryFn=(aliasInstance:any)=>aliasInstance;//从根据token获取具体的依赖resolvedDeps=[ReflectiveDependency.fromKey(ReflectiveKey.get(provider.useExisting)),];}elseif(provider.useFactory){//使用工厂方法提供依赖factoryFn=provider.useFactory;resolvedDeps=constructDependencies(provider.useFactory,provider.deps);}else{//使用提供者具体的值作为依赖factoryFn=()=>provider.useValue;resolvedDeps=_EMPTY_LIST;}//returnnewResolvedReflectiveFactory(factoryFn,resolvedDeps);}
根据不同类型的提供者,通过解析之后,得到由注入器 Injector 使用的提供者的内部解析表示形式:
exportinterfaceResolvedReflectiveProvider{//键,包括系统范围内的唯一id,以及一个tokenkey:ReflectiveKey;//可以返回由键表示的对象的实例的工厂函数resolvedFactories:ResolvedReflectiveFactory[];//指示提供者是多提供者,还是常规提供者multiProvider:boolean;}
提供者可以是服务类ClassProvider
本身,如果把服务类指定为提供者令牌,那么注入器的默认行为是用new
来实例化那个类。
Angular 中的依赖注入服务
在 Angular 中,服务就是一个带有@Injectable
装饰器的类,它封装了可以在应用程序中复用的非 UI 逻辑和代码。Angular 把组件和服务分开,是为了增进模块化程度和可复用性。
用@Injectable
标记一个类,以确保编译器将在注入类时生成必要的元数据(元数据在 Angular 中也是很重要的一部分),以创建类的依赖项。
@Injectable
装饰器的类会在编译之后,得到 Angular 可注入对象:
//根据其Injectable元数据,编译Angular可注入对象,并对结果进行修补exportfunctioncompileInjectable(type:Type<any>,srcMeta?:Injectable):void{//该编译过程依赖@angular/compiler//可参考编译器中的compileFactoryFunctioncompileInjectable实现}
Angular 中可注入对象(InjectableDef
)定义 DI 系统将如何构造 token 令牌,以及在哪些注入器(如果有)中可用:
exportinterfaceɵɵInjectableDef<T>{//指定给定类型属于特定注入器,包括root/platform/any/null以及特定的NgModuleprovidedIn:InjectorType<any>|"root"|"platform"|"any"|null;//此定义所属的令牌token:unknown;//要执行以创建可注入实例的工厂方法factory:(t?:Type<any>)=>T;//在没有显式注入器的情况下,存储可注入实例的位置value:T|undefined;}
使用@Injectable()
的providedIn
时,优化工具可以进行 Tree-shaking 优化,从而删除应用程序中未使用的服务,以减小捆绑包尺寸。
到此,关于“Angular依赖注入体系中的基本概念是什么”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注恰卡编程网网站,小编会继续努力为大家带来更多实用的文章!
推荐阅读
-
Angular生命周期执行的顺序是什么
-
Angular怎么利用service实现自定义服务
-
Angular8基础知识点有哪些
Angular8基础知识点有哪些这篇文章给大家分享的是有关Angu...
-
angular双向绑定的示例分析
-
angular父子组件通信的示例分析
-
angular中怎么使用echarts地图
angular中怎么使用echarts地图这篇文章将为大家详细讲解...
-
Angular如何实现搜索、过滤、批量删除、添加、表单验证功能
这篇文章将为大家详细讲解有关Angular如何实现搜索、过滤、批量删除、添加、表单验证功能,小编觉得挺实用的,因此分享给大家做个参...
-
Angular如何实现搜索框
这篇文章主要介绍Angular如何实现搜索框,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!1.要求:利用...
-
angular7中引用ng zorro antd的方法
-
Angular怎么创建服务
这篇文章将为大家详细讲解有关Angular怎么创建服务,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所...