目錄
依賴注入
依賴倒置原則、控制反轉、依賴注入
Angular 中的依賴注入
Injector 注入器
Provider 提供者
Angular 中的依赖注入服务
总结
首頁 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

熱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

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

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆樹的耳語 - 如何解鎖抓鉤
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1665
14
CakePHP 教程
1424
52
Laravel 教程
1321
25
PHP教程
1269
29
C# 教程
1249
24
如何在Ubuntu 24.04上安裝Angular 如何在Ubuntu 24.04上安裝Angular Mar 23, 2024 pm 12:20 PM

Angular.js是一種可自由存取的JavaScript平台,用於建立動態應用程式。它允許您透過擴展HTML的語法作為模板語言,以快速、清晰地表示應用程式的各個方面。 Angular.js提供了一系列工具,可協助您編寫、更新和測試程式碼。此外,它還提供了許多功能,如路由和表單管理。本指南將討論在Ubuntu24上安裝Angular的方法。首先,您需要安裝Node.js。 Node.js是一個基於ChromeV8引擎的JavaScript運行環境,可讓您在伺服器端執行JavaScript程式碼。要在Ub

淺析angular中怎麼使用monaco-editor 淺析angular中怎麼使用monaco-editor Oct 17, 2022 pm 08:04 PM

angular中怎麼使用monaco-editor?以下這篇文章記錄下最近的一次業務中用到的 m​​onaco-editor 在 angular 中的使用,希望對大家有幫助!

一文探究Angular中的服務端渲染(SSR) 一文探究Angular中的服務端渲染(SSR) Dec 27, 2022 pm 07:24 PM

你知道 Angular Universal 嗎?可以幫助網站提供更好的 SEO 支援哦!

如何使用PHP和Angular進行前端開發 如何使用PHP和Angular進行前端開發 May 11, 2023 pm 04:04 PM

隨著網路的快速發展,前端開發技術也不斷改進與迭代。 PHP和Angular是兩種廣泛應用於前端開發的技術。 PHP是一種伺服器端腳本語言,可以處理表單、產生動態頁面和管理存取權限等任務。而Angular是一種JavaScript的框架,可以用來開發單一頁面應用程式和建構元件化的網頁應用程式。本篇文章將介紹如何使用PHP和Angular進行前端開發,以及如何將它們

使用Angular和Node進行基於令牌的身份驗證 使用Angular和Node進行基於令牌的身份驗證 Sep 01, 2023 pm 02:01 PM

身份驗證是任何網路應用程式中最重要的部分之一。本教程討論基於令牌的身份驗證系統以及它們與傳統登入系統的差異。在本教程結束時,您將看到一個用Angular和Node.js編寫的完整工作演示。傳統身份驗證系統在繼續基於令牌的身份驗證系統之前,讓我們先來看看傳統的身份驗證系統。使用者在登入表單中提供使用者名稱和密碼,然後點擊登入。發出請求後,透過查詢資料庫在後端驗證使用者。如果請求有效,則使用從資料庫中獲取的使用者資訊建立會話,然後在回應頭中傳回會話訊息,以便將會話ID儲存在瀏覽器中。提供用於存取應用程式中受

淺析Angular中的獨立組件,看看怎麼使用 淺析Angular中的獨立組件,看看怎麼使用 Jun 23, 2022 pm 03:49 PM

這篇文章帶大家了解Angular中的獨立元件,看看怎麼在Angular中建立一個獨立元件,怎麼在獨立元件中導入已有的模組,希望對大家有幫助!

專案過大怎麼辦?如何合理拆分Angular項目? 專案過大怎麼辦?如何合理拆分Angular項目? Jul 26, 2022 pm 07:18 PM

Angular專案過大,怎麼合理拆分它?以下這篇文章跟大家介紹一下合理分割Angular專案的方法,希望對大家有幫助!

Angular元件及其顯示屬性:了解非block預設值 Angular元件及其顯示屬性:了解非block預設值 Mar 15, 2024 pm 04:51 PM

Angular框架中元件的預設顯示行為不是區塊級元素。這種設計選擇促進了元件樣式的封裝,並鼓勵開發人員有意識地定義每個元件的顯示方式。透過明確設定CSS屬性 display,Angular組件的顯示可以完全控制,從而實現所需的佈局和響應能力。

See all articles