這篇文章帶大家聊聊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 依赖注入体系中比较关键的几个概念,主要包括Injector
、Provider
和Injectable
。
对于注入器、提供者和可注入服务,我们可以简单地这样理解:
注入器用于创建依赖,会维护一个容器来管理这些依赖,并尽可能地复用它们。
一个注入器中的依赖服务,只有一个实例。
注入器需要使用提供者来管理依赖,并通过 token(DI 令牌)来进行关联。
提供者用于高速注入器应该如何获取或创建依赖。
可注入服务类会根据元数据编译后,得到可注入对象,该对象可用于创建实例。
更多编程相关知识,请访问:编程入门!!
以上是聊聊Angular 依賴注入體系中的基本概念的詳細內容。更多資訊請關注PHP中文網其他相關文章!

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

本文展示了與許可證確保的後端的前端集成,並使用Next.js構建功能性Edtech SaaS應用程序。 前端獲取用戶權限以控制UI的可見性並確保API要求遵守角色庫

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。


熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

PhpStorm Mac 版本
最新(2018.2.1 )專業的PHP整合開發工具

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器