Какие знаешь DI декораторы?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
DI-декораторы в современном JavaScript/TypeScript
В контексте фронтенд-разработки DI (Dependency Injection — внедрение зависимостей) декораторы чаще всего ассоциируются с фреймворками Angular и библиотеками, поддерживающими декораторы TypeScript. Вот основные из них:
Основные декораторы Angular
1. @Injectable()
Маркирует класс как сервис, который может быть внедрен через DI-контейнер Angular.
@Injectable({
providedIn: 'root' // или 'platform', 'any', либо в конкретном модуле
})
export class UserService {
private users: User[] = [];
getUsers(): User[] {
return this.users;
}
}
Ключевые особенности:
- providedIn: определяет область видимости сервиса
- root: singleton на уровне приложения
- platform: singleton на уровне платформы
- any: отдельный экземпляр для каждого внедряющего модуля
2. @Inject()
Позволяет внедрять зависимости, которые не являются классами (примитивы, интерфейсы, готовые объекты).
export class ApiService {
constructor(
@Inject('API_URL') private apiUrl: string,
@Inject(HttpClient) private http: HttpClient
) {}
getUsers() {
return this.http.get(`${this.apiUrl}/users`);
}
}
Использование в провайдере:
providers: [
{ provide: 'API_URL', useValue: 'https://api.example.com' }
]
3. Опциональные зависимости с @Optional()
Указывает, что зависимость может отсутствовать.
export class LoggerService {
constructor(@Optional() @Inject('LOG_LEVEL') private logLevel?: string) {
this.logLevel = logLevel || 'info';
}
}
4. @Self(), @SkipSelf(), @Host()
Декораторы для управления поиском зависимостей в иерархии инжекторов:
- @Self(): искать только в собственном инжекторе компонента
- @SkipSelf(): пропустить собственный инжектор, начать с родительского
- @Host(): остановить поиск на инжекторе текущего хоста
export class ChildComponent {
constructor(
@Self() private localService: LocalService,
@SkipSelf() private parentService: ParentService,
@Host() private hostService: HostService
) {}
}
Декораторы для внедрения в компоненты и директивы
5. @Component() и @Directive()
Хотя это не чисто DI-декораторы, они предоставляют метаданные для DI через свойство providers:
@Component({
selector: 'app-user-list',
templateUrl: './user-list.component.html',
providers: [UserService] // Сервис будет доступен только в этом компоненте и его потомках
})
export class UserListComponent {
constructor(private userService: UserService) {}
}
Расширенные паттерны DI
Кастомные декораторы для DI
Можно создавать собственные декораторы для автоматического внедрения:
export function InjectConfig(configKey: string) {
return function(target: any, propertyKey: string, parameterIndex: number) {
const configToken = `${configKey}_CONFIG`;
// Регистрируем параметр для внедрения
const injectParams = Reflect.getOwnMetadata('design:paramtypes', target) || [];
injectParams[parameterIndex] = { token: configToken };
Reflect.defineMetadata('design:paramtypes', injectParams, target);
};
}
// Использование
export class ConfigService {
constructor(@InjectConfig('api') private apiConfig: any) {}
}
DI в других фреймворках и библиотеках
InversifyJS
Библиотека для инверсии управления с поддержкой декораторов:
import { injectable, inject } from 'inversify';
@injectable()
class UserService {
constructor(@inject('Logger') private logger: ILogger) {}
}
TypeDI
Еще одна популярная библиотека для DI:
import { Service, Inject } from 'typedi';
@Service()
class UserService {
constructor(@Inject() private logger: LoggerService) {}
}
Практические аспекты использования DI декораторов
Преимущества:
- Тестируемость: легко заменять реальные зависимости моками
- Гибкость: изменение реализации без изменения клиентского кода
- Модульность: четкое разделение ответственности
- Управление жизненным циклом: контроль над созданием и уничтожением объектов
Рекомендации по использованию:
- Используйте интерфейсы для абстракции зависимостей
- Избегайте циклических зависимостей
- Используйте иерархию инжекторов для управления областью видимости сервисов
- Минимизируйте использование глобальных сервисов (
providedIn: 'root')
Современные тенденции
С появлением Angular 14+ появилась возможность использовать inject() функцию как альтернативу декораторам в конструкторе:
export class UserService {
private http = inject(HttpClient);
private apiUrl = inject('API_URL');
}
Этот подход особенно полезен:
- В функциях инициализации
- В хуках жизненного цикла
- В случаях, когда декораторы конструктора неудобны
DI декораторы остаются мощным инструментом для создания масштабируемых, поддерживаемых и тестируемых фронтенд-приложений, особенно в крупных проектах с четкой архитектурой.