在 Angular 開發(fā)中,循環(huán)依賴是一個常見且棘手的問題,而?InjectionToken?是解決循環(huán)依賴的有效手段之一。下面詳細介紹 Angular 中的循環(huán)依賴問題以及如何使用?InjectionToken?來解決它。
1. 什么是循環(huán)依賴
循環(huán)依賴指的是兩個或多個服務之間相互依賴,形成一個閉環(huán)。例如,服務 A 依賴于服務 B,而服務 B 又依賴于服務 A,這樣在創(chuàng)建這些服務的實例時,就會陷入無限循環(huán),導致程序無法正常運行。
以下是一個簡單的循環(huán)依賴示例:
import { Injectable } from '@angular/core';
@Injectable({
? providedIn: 'root'
})
export class ServiceA {
? constructor(private serviceB: ServiceB) {}
}
@Injectable({
? providedIn: 'root'
})
export class ServiceB {
? constructor(private serviceA: ServiceA) {}
}
在上述代碼中,ServiceA?的構造函數依賴于?ServiceB,而?ServiceB?的構造函數又依賴于?ServiceA,這就形成了循環(huán)依賴。當 Angular 嘗試創(chuàng)建?ServiceA?的實例時,需要先創(chuàng)建?ServiceB?的實例,而創(chuàng)建?ServiceB?的實例又需要先創(chuàng)建?ServiceA?的實例,從而陷入無限循環(huán)。
2.?InjectionToken?介紹
InjectionToken?是 Angular 提供的一種機制,用于創(chuàng)建唯一的令牌,它可以作為依賴注入的標識符。與直接使用類名作為依賴注入的令牌不同,InjectionToken?可以避免命名沖突,并且可以用于注入非類的依賴項,如配置對象、常量等。
InjectionToken?的創(chuàng)建方式如下:
import { InjectionToken } from '@angular/core';
export const MY_TOKEN = new InjectionToken<string>('MY_TOKEN');
這里創(chuàng)建了一個名為?MY_TOKEN?的?InjectionToken,它的泛型參數指定了該令牌所代表的依賴項的類型,這里是?string?類型。
3. 使用?InjectionToken?解決循環(huán)依賴
下面通過一個示例來展示如何使用?InjectionToken?解決循環(huán)依賴問題。假設我們有兩個服務?ServiceA?和?ServiceB,它們之間存在循環(huán)依賴,我們可以使用?InjectionToken?來打破這個循環(huán)。
import { Injectable, Inject, InjectionToken } from '@angular/core';
// 創(chuàng)建一個 InjectionToken 用于標識原始的 ServiceB
export const ORIGINAL_SERVICE_B = new InjectionToken<ServiceB>('ORIGINAL_SERVICE_B');
@Injectable({
? providedIn: 'root'
})
export class ServiceA {
? constructor(@Inject(ORIGINAL_SERVICE_B) private serviceB: ServiceB) {}
}
@Injectable({
? providedIn: 'root'
})
export class ServiceB {
? constructor(private serviceA: ServiceA) {}
}
然后在模塊的?providers?數組中進行配置:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { ServiceA, ServiceB, ORIGINAL_SERVICE_B } from './your-services-file';
@NgModule({
? declarations: [AppComponent],
? imports: [BrowserModule],
? providers: [
? ? { provide: ORIGINAL_SERVICE_B, useClass: ServiceB },
? ? { provide: ServiceB, useClass: ServiceB } // 這里可以根據需要替換為擴展后的 ServiceB
? ],
? bootstrap: [AppComponent]
})
export class AppModule {}
代碼解釋
創(chuàng)建?InjectionToken:ORIGINAL_SERVICE_B?是一個?InjectionToken,用于標識原始的?ServiceB。
修改?ServiceA?的構造函數:在?ServiceA?的構造函數中,使用?@Inject(ORIGINAL_SERVICE_B)?注入?ServiceB?的實例,這樣就避免了直接使用?ServiceB?類名,從而打破了循環(huán)依賴。
模塊配置:在?AppModule?的?providers?數組中,將?ORIGINAL_SERVICE_B?與?ServiceB?類關聯(lián)起來,當請求?ORIGINAL_SERVICE_B?時,Angular 會創(chuàng)建一個?ServiceB?的實例。同時,也可以根據需要將?ServiceB?替換為擴展后的服務類。
通過這種方式,我們使用?InjectionToken?成功解決了?ServiceA?和?ServiceB?之間的循環(huán)依賴問題。
其他解決循環(huán)依賴的方法
除了使用?InjectionToken,還可以通過以下方法解決循環(huán)依賴問題:
重構代碼:重新設計服務的結構,避免出現循環(huán)依賴。例如,將公共的邏輯提取到一個新的服務中,讓?ServiceA?和?ServiceB?都依賴于這個新服務。
使用 setter 注入:在構造函數中不直接注入依賴項,而是通過 setter 方法在實例創(chuàng)建后再注入依賴項。這樣可以避免在創(chuàng)建實例時就陷入循環(huán)依賴。
import { Injectable } from '@angular/core';
@Injectable({
? providedIn: 'root'
})
export class ServiceA {
? private _serviceB: ServiceB;
? set serviceB(serviceB: ServiceB) {
? ? this._serviceB = serviceB;
? }
}
@Injectable({
? providedIn: 'root'
})
export class ServiceB {
? constructor(private serviceA: ServiceA) {
? ? this.serviceA.serviceB = this;
? }
}
這種方法通過在實例創(chuàng)建后再注入依賴項,避免了構造函數中的循環(huán)依賴問題。但需要注意的是,這種方法可能會使代碼的依賴關系不夠清晰,需要謹慎使用。