站長(zhǎng)資訊網(wǎng)
        最全最豐富的資訊網(wǎng)站

        聊聊Angular 依賴注入體系中的基本概念

        本篇文章帶大家聊聊Angular,介紹一下依賴注入的基本概念,希望對(duì)大家有所幫助!

        聊聊Angular 依賴注入體系中的基本概念

        作為“為大型前端項(xiàng)目”而設(shè)計(jì)的前端框架,Angular 其實(shí)有許多值得參考和學(xué)習(xí)的設(shè)計(jì),本系列主要用于研究這些設(shè)計(jì)和功能的實(shí)現(xiàn)原理。本文主要圍繞 Angular 中的最大特點(diǎn)——依賴注入,首先來(lái)介紹一些 Angular 依賴注入體系中的基本概念。

        依賴注入

        既然要介紹 Angular 框架的依賴注入設(shè)計(jì),那么先鋪墊一下依賴注入的基本概念。我們常常會(huì)搞混依賴倒置原則(DIP)、控制反轉(zhuǎn)(IoC)、依賴注入(DI)這幾個(gè)概念,因此這里會(huì)先簡(jiǎn)單介紹一下。【相關(guān)教程推薦:《angular教程》】

        依賴倒置原則、控制反轉(zhuǎn)、依賴注入

        低耦合、高內(nèi)聚大概是每個(gè)系統(tǒng)的設(shè)計(jì)目標(biāo)之一,而為此產(chǎn)生了很多的設(shè)計(jì)模式和理念,其中便包括依賴倒置原則、控制反轉(zhuǎn)的設(shè)計(jì)思想。

        (1) 依賴倒置原則(DIP)。

        依賴倒置原則的原始定義為:

        • 高層模塊不應(yīng)該依賴低層模塊,兩者都應(yīng)該依賴其抽象;
        • 抽象不應(yīng)該依賴細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴抽象。

        簡(jiǎn)單說(shuō)便是:模塊間不應(yīng)該直接依賴對(duì)方,應(yīng)該依賴一個(gè)抽象的規(guī)則(接口或者時(shí)抽象類)。

        (2) 控制反轉(zhuǎn)(IoC)。

        控制反轉(zhuǎn)的定義為:模塊間的依賴關(guān)系從程序內(nèi)部提到外部來(lái)實(shí)例化管理。即對(duì)象在被創(chuàng)建的時(shí)候,由一個(gè)調(diào)控系統(tǒng)內(nèi)所有對(duì)象的外界實(shí)體控制,并將其所依賴的對(duì)象的引用傳遞(注入)給它。

        實(shí)現(xiàn)控制反轉(zhuǎn)主要有兩種方式:

        • 依賴注入:被動(dòng)的接收依賴對(duì)象
        • 依賴查找:主動(dòng)索取依賴的對(duì)象

        (3) 依賴注入。

        依賴注入,是控制反轉(zhuǎn)的最為常見(jiàn)的一種技術(shù)。

        依賴倒置和控制反轉(zhuǎn)兩者相輔相成,常常可以一起使用,可有效地降低模塊間的耦合。

        Angular 中的依賴注入

        在 Angular 中,同樣使用了依賴注入的技術(shù),DI 框架會(huì)在實(shí)例化某個(gè)類時(shí),向其提供這個(gè)類所聲明的依賴項(xiàng)(依賴項(xiàng):指當(dāng)類需要執(zhí)行其功能時(shí),所需要的服務(wù)或?qū)ο螅?/p>

        Angular 中的依賴注入基本上是圍繞著組件或者是模塊展開(kāi)的,主要用于給新建的組件提供依賴。

        Angular 中主要的依賴注入機(jī)制是注入器機(jī)制

        • 應(yīng)用中所需的任何依賴,都必須使用該應(yīng)用的注入器來(lái)注冊(cè)一個(gè)提供者,以便注入器可以使用這個(gè)提供者來(lái)創(chuàng)建新實(shí)例
        • Angular 會(huì)在啟動(dòng)過(guò)程中,創(chuàng)建全應(yīng)用級(jí)注入器以及所需的其它注入器

        這里面主要涉及兩個(gè)概念,分別是Injector 注入器Provider 提供商,我們來(lái)看看。

        Injector 注入器

        Injector 注入器用于創(chuàng)建依賴,會(huì)維護(hù)一個(gè)容器來(lái)管理這些依賴,并盡可能地復(fù)用它們。注入器會(huì)提供依賴的一個(gè)單例,并把這個(gè)單例對(duì)象注入到多個(gè)組件中。

        顯然,作為一個(gè)用來(lái)創(chuàng)建、管理、維護(hù)依賴的容器,注入器的功能很簡(jiǎn)單:創(chuàng)建依賴實(shí)例、獲取依賴實(shí)例、管理依賴實(shí)例。我們也可以從抽象類Injector的源碼中看出來(lái):

        export abstract class Injector {   // 找不到依賴   static THROW_IF_NOT_FOUND = THROW_IF_NOT_FOUND;   // NullInjector 是樹(shù)的頂部   // 如果你在樹(shù)中向上走了很遠(yuǎn),以至于要在 NullInjector 中尋找服務(wù),那么將收到錯(cuò)誤消息,或者對(duì)于 @Optional(),返回 null   static NULL: Injector = new NullInjector();    // 根據(jù)提供的 Token 從 Injector 檢索實(shí)例   abstract get<T>(     token: Type<T> | AbstractType<T> | InjectionToken<T>,     notFoundValue?: T,     flags?: InjectFlags   ): T;    // 創(chuàng)建一個(gè)新的 Injector 實(shí)例,該實(shí)例提供一個(gè)或多個(gè)依賴項(xiàng)   static create(options: {     providers: StaticProvider[];     parent?: Injector;     name?: string;   }): Injector;    // ??defineInjectable 用于構(gòu)造一個(gè) InjectableDef   // 它定義 DI 系統(tǒng)將如何構(gòu)造 Token,并且在哪些 Injector 中可用   static ?prov = ??defineInjectable({     token: Injector,     providedIn: "any" as any,     // ??inject 生成的指令:從當(dāng)前活動(dòng)的 Injector 注入 Token     factory: () => ??inject(INJECTOR),   });    static __NG_ELEMENT_ID__ = InjectorMarkers.Injector; }

        也就是說(shuō),我們可以將需要共享的依賴實(shí)例添加到注入器中,并通過(guò) Token 查詢和檢索注入器來(lái)獲取相應(yīng)的依賴實(shí)例。

        需要注意的是,Angular 中的注入器是分層的,因此查找依賴的過(guò)程也是向上遍歷注入器樹(shù)的過(guò)程。

        這是因?yàn)樵?Angular 中,應(yīng)用是以模塊的方式組織的,具體可以參考5.模塊化組織篇。一般來(lái)說(shuō),頁(yè)面的 DOM 是以html作為根節(jié)點(diǎn)的樹(shù)狀結(jié)構(gòu),以此為基礎(chǔ),Angular 應(yīng)用中的組件和模塊也是與之相伴的樹(shù)狀結(jié)構(gòu)。

        而注入器服務(wù)于組件和模塊,同樣是掛載與模塊和組織上的樹(shù)狀結(jié)構(gòu)。因此,Injector 也劃分為模塊和組件級(jí)別,可分別為組件和模塊提供依賴的具體實(shí)例。注入器是可繼承的,這意味著如果指定的注入器無(wú)法解析某個(gè)依賴,它就會(huì)請(qǐng)求父注入器來(lái)解析它,我們同樣可以從上面的創(chuàng)建注入器代碼中看到:

        // 創(chuàng)建一個(gè)新的 Injector 實(shí)例,可傳入 parent 父注入器 static create(options: {providers: StaticProvider[], parent?: Injector, name?: string}): Injector;

        在某個(gè)注入器的范圍內(nèi),服務(wù)是單例的。也就是說(shuō),在指定的注入器中最多只有某個(gè)服務(wù)的最多一個(gè)實(shí)例。如果不希望在所有地方都使用該服務(wù)的同一個(gè)實(shí)例,則可以通過(guò)注冊(cè)多個(gè)注入器、并按照需要關(guān)聯(lián)到組件和模塊中的方式,來(lái)按需共享某個(gè)服務(wù)依賴的實(shí)例。

        我們可以看到創(chuàng)建一個(gè)新的Injector實(shí)例時(shí),傳入的參數(shù)包括Provider,這是因?yàn)?code>Injector不會(huì)直接創(chuàng)建依賴,而是通過(guò)Provider來(lái)完成的。每個(gè)注入器會(huì)維護(hù)一個(gè)提供者的列表,并根據(jù)組件或其它服務(wù)的需要,用它們來(lái)提供服務(wù)的實(shí)例。

        Provider 提供者

        Provider 提供者用來(lái)告訴注入器應(yīng)該如何獲取或創(chuàng)建依賴,要想讓注入器能夠創(chuàng)建服務(wù)(或提供其它類型的依賴),必須使用某個(gè)提供者配置好注入器。

        一個(gè)提供者對(duì)象定義了如何獲取與 DI 令牌(token) 相關(guān)聯(lián)的可注入依賴,而注入器會(huì)使用這個(gè)提供者來(lái)創(chuàng)建它所依賴的那些類的實(shí)例。

        關(guān)于 DI 令牌:

        • 當(dāng)使用提供者配置注入器時(shí),就會(huì)把提供者和一個(gè) DI 令牌關(guān)聯(lián)起來(lái);
        • 注入器維護(hù)一個(gè)內(nèi)部令牌-提供者的映射表,當(dāng)請(qǐng)求一個(gè)依賴項(xiàng)時(shí)就會(huì)引用它,令牌就是這個(gè)映射表的鍵。

        提供者的類型很多,從官方文檔中可以閱讀它們的具體定義:

        export type Provider =   | TypeProvider   | ValueProvider   | ClassProvider   | ConstructorProvider   | ExistingProvider   | FactoryProvider   | any[];

        提供者的解析過(guò)程如下:

        function resolveReflectiveFactory(   provider: NormalizedProvider ): ResolvedReflectiveFactory {   let factoryFn: Function;   let resolvedDeps: ReflectiveDependency[];   if (provider.useClass) {     // 使用類來(lái)提供依賴     const useClass = resolveForwardRef(provider.useClass);     factoryFn = reflector.factory(useClass);     resolvedDeps = _dependenciesFor(useClass);   } else if (provider.useExisting) {     // 使用已有依賴     factoryFn = (aliasInstance: any) => aliasInstance;     // 從根據(jù) token 獲取具體的依賴     resolvedDeps = [       ReflectiveDependency.fromKey(ReflectiveKey.get(provider.useExisting)),     ];   } else if (provider.useFactory) {     // 使用工廠方法提供依賴     factoryFn = provider.useFactory;     resolvedDeps = constructDependencies(provider.useFactory, provider.deps);   } else {     // 使用提供者具體的值作為依賴     factoryFn = () => provider.useValue;     resolvedDeps = _EMPTY_LIST;   }   //   return new ResolvedReflectiveFactory(factoryFn, resolvedDeps); }

        根據(jù)不同類型的提供者,通過(guò)解析之后,得到由注入器 Injector 使用的提供者的內(nèi)部解析表示形式:

        export interface ResolvedReflectiveProvider {   // 鍵,包括系統(tǒng)范圍內(nèi)的唯一 id,以及一個(gè) token   key: ReflectiveKey;   // 可以返回由鍵表示的對(duì)象的實(shí)例的工廠函數(shù)   resolvedFactories: ResolvedReflectiveFactory[];   // 指示提供者是多提供者,還是常規(guī)提供者   multiProvider: boolean; }

        提供者可以是服務(wù)類ClassProvider本身,如果把服務(wù)類指定為提供者令牌,那么注入器的默認(rèn)行為是用new來(lái)實(shí)例化那個(gè)類。

        Angular 中的依賴注入服務(wù)

        在 Angular 中,服務(wù)就是一個(gè)帶有@Injectable裝飾器的類,它封裝了可以在應(yīng)用程序中復(fù)用的非 UI 邏輯和代碼。Angular 把組件和服務(wù)分開(kāi),是為了增進(jìn)模塊化程度和可復(fù)用性。

        @Injectable標(biāo)記一個(gè)類,以確保編譯器將在注入類時(shí)生成必要的元數(shù)據(jù)(元數(shù)據(jù)在 Angular 中也是很重要的一部分),以創(chuàng)建類的依賴項(xiàng)。

        @Injectable裝飾器的類會(huì)在編譯之后,得到 Angular 可注入對(duì)象:

        // 根據(jù)其 Injectable 元數(shù)據(jù),編譯 Angular 可注入對(duì)象,并對(duì)結(jié)果進(jìn)行修補(bǔ) export function compileInjectable(type: Type<any>, srcMeta?: Injectable): void {   // 該編譯過(guò)程依賴 @angular/compiler   // 可參考編譯器中的 compileFactoryFunction compileInjectable 實(shí)現(xiàn) }

        Angular 中可注入對(duì)象(InjectableDef)定義 DI 系統(tǒng)將如何構(gòu)造 token 令牌,以及在哪些注入器(如果有)中可用:

        export interface ??InjectableDef<T> {   // 指定給定類型屬于特定注入器,包括 root/platform/any/null 以及特定的 NgModule   providedIn: InjectorType<any> | "root" | "platform" | "any" | null;   // 此定義所屬的令牌   token: unknown;   // 要執(zhí)行以創(chuàng)建可注入實(shí)例的工廠方法   factory: (t?: Type<any>) => T;   // 在沒(méi)有顯式注入器的情況下,存儲(chǔ)可注入實(shí)例的位置   value: T | undefined; }

        使用@Injectable()providedIn時(shí),優(yōu)化工具可以進(jìn)行 Tree-shaking 優(yōu)化,從而刪除應(yīng)用程序中未使用的服務(wù),以減小捆綁包尺寸。

        總結(jié)

        本文簡(jiǎn)單介紹了在 Angular 依賴注入體系中比較關(guān)鍵的幾個(gè)概念,主要包括InjectorProviderInjectable

        對(duì)于注入器、提供者和可注入服務(wù),我們可以簡(jiǎn)單地這樣理解:

        • 注入器用于創(chuàng)建依賴,會(huì)維護(hù)一個(gè)容器來(lái)管理這些依賴,并盡可能地復(fù)用它們。

        • 一個(gè)注入器中的依賴服務(wù),只有一個(gè)實(shí)例。

        • 注入器需要使用提供者來(lái)管理依賴,并通過(guò) token(DI 令牌)來(lái)進(jìn)行關(guān)聯(lián)。

        • 提供者用于高速注入器應(yīng)該如何獲取或創(chuàng)建依賴。

        • 可注入服務(wù)類會(huì)根據(jù)元數(shù)據(jù)編譯后,得到可注入對(duì)象,該對(duì)象可用于創(chuàng)建實(shí)例。

        贊(0)
        分享到: 更多 (0)
        網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)
        主站蜘蛛池模板: 久久99国内精品自在现线| 在线精品动漫一区二区无广告| 亚洲国产精品福利片在线观看| 亚洲精品NV久久久久久久久久| 午夜在线视频91精品| 久久亚洲私人国产精品| 国产夫妇精品自在线| 99re只有精品8中文| 欧美激情精品久久久久久久九九九| 日本午夜精品理论片A级APP发布| 少妇精品久久久一区二区三区 | 精品国产AⅤ一区二区三区4区| 中文无码精品一区二区三区| 9久久9久久精品| 亚洲av午夜成人片精品电影| 99久久成人国产精品免费| 欧美亚洲日本久久精品| 国产精品福利在线观看| 久久精品无码免费不卡| 精品亚洲麻豆1区2区3区| 97精品伊人久久久大香线蕉| 午夜精品久久久久久久| 久久久久国产成人精品亚洲午夜| 99re6这里有精品热视频| 日韩精品电影一区亚洲| 99久久精品无码一区二区毛片| 日韩精品久久久肉伦网站| 国产专区日韩精品欧美色| 国产精品亚洲精品| 久久精品亚洲中文字幕无码麻豆| 国产精品亚洲美女久久久| 高清在线亚洲精品国产二区| 久久久久久国产精品免费无码| 色哟哟国产精品免费观看| 国产精品无码免费专区午夜| 老司机91精品网站在线观看| 国产伦精品免编号公布| 亚洲国产精品成人精品无码区在线| 精品人妻伦一二三区久久| 国产区精品高清在线观看| 99热日韩这里只有精品|