搜尋
首頁web前端js教程聊聊Angular 依賴注入體系中的基本概念

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

Mar 03, 2022 am 11:06 AM
angular依賴注入

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

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

作為「為大型前端專案」而設計的前端框架,Angular 其實有許多值得參考和學習的設計,本系列主要用於研究這些設計和功能的實現原理。本文主要圍繞在 Angular 中的最大特點──依賴注入,首先來介紹一些 Angular 依賴注入系統中的基本概念。

依賴注入

既然要介紹 Angular 框架的依賴注入設計,那麼先鋪墊一下依賴注入的基本概念。我們常常會搞混依賴倒置原則(DIP)、控制反轉(IoC)、依賴注入(DI)這幾個概念,因此這裡會先簡單介紹一下。 【相關教學推薦:《angular教學》】

依賴倒置原則、控制反轉、依賴注入

低耦合、高內聚大概是每個系統的設計目標之一,而為此產生了許多的設計模式和理念,其中便包括依賴倒置原則、控制反轉的設計思想。

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

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

  • 高層模組不應該依賴低層模組,兩者都應該依賴其抽象;
  • #抽像不應該依賴細節,細節應該依賴抽象。

簡單來說就是:模組間不應該直接依賴對方,應該依賴一個抽象的規則(介面或時抽象類別)。

(2) 控制反轉(IoC)。

控制反轉的定義為:模組間的依賴關係從程式內部提到外部來實例化管理。即物件在被創建的時候,由一個調控系統內所有物件的外界實體控制,並將其所依賴的物件的引用傳遞(注入)給它。

實現控制反轉主要有兩種方式:

  • 依賴注入:被動的接收依賴物件
  • 依賴查找:主動索取依賴的物件

(3) 依賴注入。

依賴注入,是控制反轉的最常見的一種技術。

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

Angular 中的依賴注入

在Angular 中,同樣使用了依賴注入的技術,DI 框架會在實例化某個類別時,向其提供這個類別所聲明的依賴項(依賴項:指當類別需要執行其功能時,所需要的服務或物件)。

Angular 中的依賴注入基本上是圍繞著元件或是模組展開的,主要用於為新建的元件提供依賴。

Angular 中主要的依賴注入機制是注入器機制

  • 應用程式中所需的任何依賴,都必須使用該應用程式的注入器來註冊一個提供者,以便注入器可以使用這個提供者來創建新實例
  • Angular 會在啟動過程中,創建全應用級注入器以及所需的其它注入器

這裡面主要涉及兩個概念,分別是Injector 注入器Provider 提供者,我們來看看。

Injector 注入器

Injector 注入器用於建立依賴,會維護一個容器來管理這些依賴,並盡可能地重複使用它們。注入器會提供依賴的一個單例,並把這個單例物件注入到多個元件中。

顯然,作為一個用來建立、管理、維護依賴的容器,注入器的功能很簡單:建立依賴實例、取得依賴實例、管理依賴實例。我們也可以從抽象類別Injector的原始碼中看出來:

export abstract class Injector {
  // 找不到依赖
  static THROW_IF_NOT_FOUND = THROW_IF_NOT_FOUND;
  // NullInjector 是树的顶部
  // 如果你在树中向上走了很远,以至于要在 NullInjector 中寻找服务,那么将收到错误消息,或者对于 @Optional(),返回 null
  static NULL: Injector = new NullInjector();

  // 根据提供的 Token 从 Injector 检索实例
  abstract get<T>(
    token: Type<T> | AbstractType<T> | InjectionToken<T>,
    notFoundValue?: T,
    flags?: InjectFlags
  ): T;

  // 创建一个新的 Injector 实例,该实例提供一个或多个依赖项
  static create(options: {
    providers: StaticProvider[];
    parent?: Injector;
    name?: string;
  }): Injector;

  // ɵɵdefineInjectable 用于构造一个 InjectableDef
  // 它定义 DI 系统将如何构造 Token,并且在哪些 Injector 中可用
  static ɵprov = ɵɵdefineInjectable({
    token: Injector,
    providedIn: "any" as any,
    // ɵɵinject 生成的指令:从当前活动的 Injector 注入 Token
    factory: () => ɵɵinject(INJECTOR),
  });

  static __NG_ELEMENT_ID__ = InjectorMarkers.Injector;
}

#也就是說,我們可以將需要共享的依賴實例加入註入器中,並透過Token 查詢和檢索注入器來取得對應的依賴實例。

要注意的是,Angular 中的注入器是分層的,因此尋找依賴的過程也是向上遍歷注入器樹的過程。

這是因為在 Angular 中,應用是以模組的方式組織的,具體可以參考5.模組化組織篇。一般來說,頁面的 DOM 是以html作為根節點的樹狀結構,以此為基礎,Angular 應用中的元件和模組也是與之相伴的樹狀結構。

而注入器服務於元件和模組,同樣是掛載與模組和組織上的樹狀結構。因此,Injector 也劃分為模組和組件級別,可分別為組件和模組提供依賴的具體實例。注入器是可繼承的,這意味著如果指定的注入器無法解析某個依賴,它就會請求父注入器來解析它,我們同樣可以從上面的建立注入器程式碼中看到:

// 创建一个新的 Injector 实例,可传入 parent 父注入器
static create(options: {providers: StaticProvider[], parent?: Injector, name?: string}): Injector;

在某个注入器的范围内,服务是单例的。也就是说,在指定的注入器中最多只有某个服务的最多一个实例。如果不希望在所有地方都使用该服务的同一个实例,则可以通过注册多个注入器、并按照需要关联到组件和模块中的方式,来按需共享某个服务依赖的实例。

我们可以看到创建一个新的Injector实例时,传入的参数包括Provider,这是因为Injector不会直接创建依赖,而是通过Provider来完成的。每个注入器会维护一个提供者的列表,并根据组件或其它服务的需要,用它们来提供服务的实例。

Provider 提供者

Provider 提供者用来告诉注入器应该如何获取或创建依赖,要想让注入器能够创建服务(或提供其它类型的依赖),必须使用某个提供者配置好注入器。

一个提供者对象定义了如何获取与 DI 令牌(token) 相关联的可注入依赖,而注入器会使用这个提供者来创建它所依赖的那些类的实例。

关于 DI 令牌:

  • 当使用提供者配置注入器时,就会把提供者和一个 DI 令牌关联起来;
  • 注入器维护一个内部令牌-提供者的映射表,当请求一个依赖项时就会引用它,令牌就是这个映射表的键。

提供者的类型很多,从官方文档中可以阅读它们的具体定义:

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

提供者的解析过程如下:

function resolveReflectiveFactory(
  provider: NormalizedProvider
): ResolvedReflectiveFactory {
  let factoryFn: Function;
  let resolvedDeps: ReflectiveDependency[];
  if (provider.useClass) {
    // 使用类来提供依赖
    const useClass = resolveForwardRef(provider.useClass);
    factoryFn = reflector.factory(useClass);
    resolvedDeps = _dependenciesFor(useClass);
  } else if (provider.useExisting) {
    // 使用已有依赖
    factoryFn = (aliasInstance: any) => aliasInstance;
    // 从根据 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);
}

根据不同类型的提供者,通过解析之后,得到由注入器 Injector 使用的提供者的内部解析表示形式:

export interface ResolvedReflectiveProvider {
  // 键,包括系统范围内的唯一 id,以及一个 token
  key: ReflectiveKey;
  // 可以返回由键表示的对象的实例的工厂函数
  resolvedFactories: ResolvedReflectiveFactory[];
  // 指示提供者是多提供者,还是常规提供者
  multiProvider: boolean;
}

提供者可以是服务类ClassProvider本身,如果把服务类指定为提供者令牌,那么注入器的默认行为是用new来实例化那个类。

Angular 中的依赖注入服务

在 Angular 中,服务就是一个带有@Injectable装饰器的类,它封装了可以在应用程序中复用的非 UI 逻辑和代码。Angular 把组件和服务分开,是为了增进模块化程度和可复用性。

@Injectable标记一个类,以确保编译器将在注入类时生成必要的元数据(元数据在 Angular 中也是很重要的一部分),以创建类的依赖项。

@Injectable装饰器的类会在编译之后,得到 Angular 可注入对象:

// 根据其 Injectable 元数据,编译 Angular 可注入对象,并对结果进行修补
export function compileInjectable(type: Type<any>, srcMeta?: Injectable): void {
  // 该编译过程依赖 @angular/compiler
  // 可参考编译器中的 compileFactoryFunction compileInjectable 实现
}

Angular 中可注入对象(InjectableDef)定义 DI 系统将如何构造 token 令牌,以及在哪些注入器(如果有)中可用:

export interface ɵɵInjectableDef<T> {
  // 指定给定类型属于特定注入器,包括 root/platform/any/null 以及特定的 NgModule
  providedIn: InjectorType<any> | "root" | "platform" | "any" | null;
  // 此定义所属的令牌
  token: unknown;
  // 要执行以创建可注入实例的工厂方法
  factory: (t?: Type<any>) => T;
  // 在没有显式注入器的情况下,存储可注入实例的位置
  value: T | undefined;
}

使用@Injectable()providedIn时,优化工具可以进行 Tree-shaking 优化,从而删除应用程序中未使用的服务,以减小捆绑包尺寸。

总结

本文简单介绍了在 Angular 依赖注入体系中比较关键的几个概念,主要包括InjectorProviderInjectable

对于注入器、提供者和可注入服务,我们可以简单地这样理解:

  • 注入器用于创建依赖,会维护一个容器来管理这些依赖,并尽可能地复用它们。

  • 一个注入器中的依赖服务,只有一个实例。

  • 注入器需要使用提供者来管理依赖,并通过 token(DI 令牌)来进行关联。

  • 提供者用于高速注入器应该如何获取或创建依赖。

  • 可注入服务类会根据元数据编译后,得到可注入对象,该对象可用于创建实例。

更多编程相关知识,请访问:编程入门!!

以上是聊聊Angular 依賴注入體系中的基本概念的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:掘金社区。如有侵權,請聯絡admin@php.cn刪除
JavaScript數據類型:瀏覽器和nodejs之間是否有區別?JavaScript數據類型:瀏覽器和nodejs之間是否有區別?May 14, 2025 am 12:15 AM

JavaScript核心數據類型在瀏覽器和Node.js中一致,但處理方式和額外類型有所不同。 1)全局對像在瀏覽器中為window,在Node.js中為global。 2)Node.js獨有Buffer對象,用於處理二進制數據。 3)性能和時間處理在兩者間也有差異,需根據環境調整代碼。

JavaScript評論:使用//和 / * * / * / * /JavaScript評論:使用//和 / * * / * / * /May 13, 2025 pm 03:49 PM

JavaScriptusestwotypesofcomments:single-line(//)andmulti-line(//).1)Use//forquicknotesorsingle-lineexplanations.2)Use//forlongerexplanationsorcommentingoutblocksofcode.Commentsshouldexplainthe'why',notthe'what',andbeplacedabovetherelevantcodeforclari

Python vs. JavaScript:開發人員的比較分析Python vs. JavaScript:開發人員的比較分析May 09, 2025 am 12:22 AM

Python和JavaScript的主要區別在於類型系統和應用場景。 1.Python使用動態類型,適合科學計算和數據分析。 2.JavaScript採用弱類型,廣泛用於前端和全棧開發。兩者在異步編程和性能優化上各有優勢,選擇時應根據項目需求決定。

Python vs. JavaScript:選擇合適的工具Python vs. JavaScript:選擇合適的工具May 08, 2025 am 12:10 AM

選擇Python還是JavaScript取決於項目類型:1)數據科學和自動化任務選擇Python;2)前端和全棧開發選擇JavaScript。 Python因其在數據處理和自動化方面的強大庫而備受青睞,而JavaScript則因其在網頁交互和全棧開發中的優勢而不可或缺。

Python和JavaScript:了解每個的優勢Python和JavaScript:了解每個的優勢May 06, 2025 am 12:15 AM

Python和JavaScript各有優勢,選擇取決於項目需求和個人偏好。 1.Python易學,語法簡潔,適用於數據科學和後端開發,但執行速度較慢。 2.JavaScript在前端開發中無處不在,異步編程能力強,Node.js使其適用於全棧開發,但語法可能複雜且易出錯。

JavaScript的核心:它是在C還是C上構建的?JavaScript的核心:它是在C還是C上構建的?May 05, 2025 am 12:07 AM

javascriptisnotbuiltoncorc; sanInterpretedlanguagethatrunsonenginesoftenwritteninc.1)JavascriptwasdesignedAsignedAsalightWeight,drackendedlanguageforwebbrowsers.2)Enginesevolvedfromsimpleterterpretpretpretpretpreterterpretpretpretpretpretpretpretpretpretcompilerers,典型地,替代品。

JavaScript應用程序:從前端到後端JavaScript應用程序:從前端到後端May 04, 2025 am 12:12 AM

JavaScript可用於前端和後端開發。前端通過DOM操作增強用戶體驗,後端通過Node.js處理服務器任務。 1.前端示例:改變網頁文本內容。 2.後端示例:創建Node.js服務器。

Python vs. JavaScript:您應該學到哪種語言?Python vs. JavaScript:您應該學到哪種語言?May 03, 2025 am 12:10 AM

選擇Python還是JavaScript應基於職業發展、學習曲線和生態系統:1)職業發展:Python適合數據科學和後端開發,JavaScript適合前端和全棧開發。 2)學習曲線:Python語法簡潔,適合初學者;JavaScript語法靈活。 3)生態系統:Python有豐富的科學計算庫,JavaScript有強大的前端框架。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境