Angular提供了一個強大的依賴注入(Dependency Injection,簡稱DI)系統,使得元件之間的依賴關系變得清晰,可管理,進而提高了代碼的可讀性和可維護性。今天就來談談Angular依賴注入系統。
一、什麼是依賴注入?
依賴注入是一種設計模式,它處理了對象之間的依賴關系。在沒有依賴注入的情況下,一個對象需要自己建立或者查找它所需要的其他對象。然而,這種方式會導緻對象之間的耦合度增加,當一個對象改變時,可能會影響到其他的對象,進而增加了代碼的複雜度。
相比之下,依賴注入通過将依賴對象的建立和查找工作交給依賴注入系統,使得對象之間的耦合度大大減小。這樣,每個對象隻需要關注自己的職責,而無需關心它的依賴對象如何建立和查找,進而提高了代碼的可讀性和可維護性。
二、依賴注入原理
Angular通過使用裝飾器(Decorator)和中繼資料(Metadata)來實作依賴注入。
首先來看看裝飾器。在Angular中,裝飾器是一種特殊的聲明,它可以修改類、方法、屬性或參數的行為。Angular使用裝飾器來标記和修改類,使得這些類可以被依賴注入系統管理。例如,@Injectable()裝飾器告訴Angular這個類可以被依賴注入系統管理。
@Injectable({
providedIn: 'root'
})
export class MyService {
// ...
}
其次來看看中繼資料。中繼資料是關于類、方法、屬性或參數的資料。Angular使用中繼資料來了解如何建立和管理類。例如,providers中繼資料告訴Angular如何建立類的執行個體。
@NgModule({
providers: [
MyService
]
})
export class AppModule { }
在這個例子中,providers中繼資料告訴Angular,當需要MyService的執行個體時,應該建立一個MyService的執行個體。
現在我們已經了解了Angular使用裝飾器和中繼資料來實作依賴注入,接下來我們來看看Angular的依賴注入系統是如何工作的。
首先,當Angular需要一個類的執行個體時,它會首先檢查這個類是否有@Injectable()裝飾器。如果有,那麼Angular就會通過調用這個類的構造函數來建立一個新的執行個體。在調用構造函數時,Angular會檢查構造函數的參數。對于每一個參數,Angular會嘗試從依賴注入系統中擷取一個比對的執行個體。
下面這個示例,展示了如何在TypeScript中使用Angular的依賴注入系統:
import 'reflect-metadata';
import { ReflectiveInjector, Injectable, Injector } from 'injection-js';
class Http {}
@Injectable()
class Service {
constructor(private http: Http) {}
}
@Injectable()
class Service2 {
constructor(private injector: Injector) {}
getService(): void {
console.log(this.injector.get(Service) instanceof Service);
}
createChildInjector(): void {
const childInjector = ReflectiveInjector.resolveAndCreate([Service], this.injector);
}
}
const injector = ReflectiveInjector.resolveAndCreate([Service, Http]);
console.log(injector.get(Service) instanceof Service);
在這個示例中,Service類依賴于Http類。當Angular需要一個Service的執行個體時,它會通過調用Service的構造函數來建立一個新的執行個體。在調用構造函數時,Angular會嘗試從依賴注入系統中擷取一個Http的執行個體。如果成功,那麼Angular就會将這個Http的執行個體作為參數傳遞給Service的構造函數。
Angular的依賴注入系統還支援多級注入。這意味着一個類可以依賴于其他類,而這些類又可以依賴于其他的類,以此類推。在這種情況下,Angular會遞歸地處理這些依賴關系,直到所有的依賴都被解決。
除此之外,Angular的依賴注入系統還提供了一些進階的特性,比如提供器(providers)、注入令牌(InjectionToken)、可選的依賴項(@Optional())、自我注入(@Self())等等。它們可以讓我們更靈活、更高效地使用Angular的DI系統,使得依賴注入系統更加靈活和強大。
三、提供器(Providers)
提供器是告訴Angular如何建立依賴項的說明。最簡單的提供器就是類提供器,它告訴Angular可以使用new關鍵字來建立依賴項。例如,當我們在@Injectable()裝飾器中設定providedIn: 'root'時,就是告訴Angular要為根注入器建立一個該服務的執行個體。這實際上就是使用了一個類提供器,如下所示:
{ provide: MyService, useClass: MyService }
此外,Angular的DI系統還支援其他幾種類型的提供器,如值提供器、工廠提供器和别名提供器,它們可以讓我們更靈活地建立和配置依賴項,我會在後續文章中做詳細介紹。
四、 注入令牌(InjectionToken)
有時,我們可能需要注入一些不是類的值,例如配置對象、基礎類型的值等。為了能夠讓Angular的DI系統識别這些值,我們可以使用InjectionToken來建立一個注入令牌。
import { InjectionToken } from '@angular/core';
export const MY_CONFIG_TOKEN = new InjectionToken<string>('My Config');
然後,我們可以在提供器中使用這個令牌來提供一個值:
{ provide: MY_CONFIG_TOKEN, useValue: 'My Config Value' }
五、可選的依賴項(@Optional())
在預設情況下,如果Angular的DI系統找不到一個依賴項,就會抛出一個錯誤。但有時,我們可能希望在找不到依賴項時,也能讓應用正常運作。這時,就可以使用@Optional()裝飾器來标記這個依賴項是可選的。
constructor(@Optional() private myService: MyService) { }
六、自我注入(@Self())
預設情況下,Angular的DI系統會在目前注入器和其所有父注入器中查找依賴項。如果我們希望Angular隻在目前注入器中查找依賴項,就可以使用@Self()裝飾器。
constructor(@Self() private myService: MyService) { }
七、跳過自身(@SkipSelf())
相反,如果我們希望Angular跳過目前注入器,隻在其父注入器中查找依賴項,就可以使用@SkipSelf()裝飾器。
constructor(@SkipSelf() private myService: MyService) { }
以上就是Angular依賴注入系統的基本介紹,我們可以更好地控制依賴項的建立和注入,進而編寫出更靈活、更可維護的代碼。後續文章會對細節做詳細介紹,歡迎大家持續關注。#前端架構##前端面試##暑期創作大賽#