這篇文章帶大家聊聊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中文網其他相關文章!

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

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

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

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

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

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

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

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


熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

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

熱門文章

熱工具

WebStorm Mac版
好用的JavaScript開發工具

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

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

SublimeText3漢化版
中文版,非常好用

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