Розширені патерни впровадження залежностей в Angular
Розширені патерни DI
Система впровадження залежностей Angular виходить за межі базового впровадження сервісів. Розуміння розширених патернів є критично важливим для створення масштабованих додатків.
Багато постачальників
Зареєструйте кілька значень для одного й того ж токена:
const HTTP_INTERCEPTORS = new InjectionToken<HttpInterceptor[]>('interceptors');
// Кожен постачальник ДОДАЄ до масиву
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
]
// Впровадження отримує ВСІ з них
constructor(@Inject(HTTP_INTERCEPTORS) private interceptors: HttpInterceptor[]) {
// interceptors = [AuthInterceptor, LoggingInterceptor, ErrorInterceptor]
}Фабричні постачальники
Створюйте залежності з динамічною логікою:
{ provide: LoggerService, useFactory: () => {
const env = inject(EnvironmentService);
return env.isProduction
? new ProductionLogger()
: new ConsoleLogger();
}
}Опціональні та Self/SkipSelf
@Component({ /* ... */ })
export class MyComponent {
// Не викличе помилку, якщо не знайдено
constructor(@Optional() private analytics?: AnalyticsService) {}
// Шукає тільки в ІН'ЄКТОРІ цього компонента
constructor(@Self() private service: MyService) {}
// Пропускає ЦЕЙ ін'єктор, шукає в батьківському
constructor(@SkipSelf() private service: MyService) {}
}Tree-Shakeable постачальники
// ✅ Tree-shakeable — видаляється, якщо не впроваджено ніде
@Injectable({ providedIn: 'root' })
export class UserService {}
// ❌ Не tree-shakeable — завжди включається
@NgModule({ providers: [UserService] })
export class UserModule {}Постачальники на рівні компонента
// Кожен екземпляр компонента отримує СВІЙ власний екземпляр сервісу
@Component({
providers: [FormStateService] // Не ділиться з іншими компонентами
})
export class FormComponent {
constructor(private formState: FormStateService) {}
// Кожен FormComponent має свій власний FormStateService
}Абстрактний клас як токен
// Визначити абстрактний контракт
abstract class Storage {
abstract get(key: string): string | null;
abstract set(key: string, value: string): void;
}
// Реалізація
class LocalStorageService extends Storage {
get(key: string) { return localStorage.getItem(key); }
set(key: string, value: string) { localStorage.setItem(key, value); }
}
// Надати реалізацію
{ provide: Storage, useClass: LocalStorageService }
// Впровадження абстракції
constructor(private storage: Storage) {
this.storage.get('token'); // Використовує LocalStorageService
}Конфігурація на основі середовища
export const APP_CONFIG = new InjectionToken<AppConfig>('app.config');
// У main.ts
bootstrapApplication(AppComponent, {
providers: [
{
provide: APP_CONFIG,
useValue: {
apiUrl: environment.apiUrl,
debug: !environment.production,
features: environment.features,
}
}
]
});
// У будь-якому сервісі/компоненті
private config = inject(APP_CONFIG);Важливо:
Розширені патерни DI включають багато постачальників (системи плагінів, перехоплювачі), фабричні постачальники (умовна логіка) та абстрактні класи токенів (чисті інтерфейси). Використовуйте providedIn: 'root' для tree-shakeable синглтонів. Використовуйте постачальників на рівні компонента для ізольованих екземплярів. Розуміння ієрархії DI (Self, SkipSelf, Optional) є ключовим для складних додатків.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.