Dependency Injection (DI) — это подход к организации зависимостей в приложении, который облегчает тестирование, повторное использование кода и управление сложными компонентами. В контексте Inferno, легковесного и высокопроизводительного фреймворка для построения пользовательских интерфейсов на JavaScript, DI помогает разделять логику компонентов и их зависимости, минимизируя жёсткую связность.
Инверсия управления (IoC) DI строится на концепции инверсии управления: компоненты не создают свои зависимости напрямую, а получают их извне. Это позволяет менять реализацию зависимостей без модификации самого компонента.
Отделение интерфейса от реализации Компоненты оперируют абстракциями (интерфейсами), а конкретные реализации внедряются через DI. Такой подход упрощает подмену реализаций для тестирования или разных конфигураций приложения.
Контейнер зависимостей Для управления зависимостями создаётся специальный объект-контейнер, который хранит соответствия между интерфейсами и их реализациями. Контейнер отвечает за создание экземпляров и внедрение их в компоненты.
Inferno сам по себе не предоставляет встроенного механизма DI, как Angular, поэтому подход основан на чистом JavaScript и архитектурных паттернах.
Простейший контейнер может быть реализован через объект с регистрацией и резолвингом зависимостей:
class Container {
constructor() {
this.services = new Map();
}
register(name, implementation) {
this.services.set(name, implementation);
}
resolve(name) {
const service = this.services.get(name);
if (!service) throw new Error(`Service ${name} not registered`);
return service;
}
}
Регистрация сервисов и их внедрение:
class ApiService {
fetchData() {
return Promise.resolve({ data: 'example' });
}
}
class UserComponent {
constructor(apiService) {
this.apiService = apiService;
}
render() {
this.apiService.fetchData().then(data => console.log(data));
return null; // Inferno компонент может вернуть JSX или null
}
}
const container = new Container();
container.register('apiService', new ApiService());
const apiService = container.resolve('apiService');
const component = new UserComponent(apiService);
component.render();
В Inferno компоненты могут получать зависимости через пропсы, что упрощает интеграцию с DI-контейнером:
function UserComponent({ apiService }) {
apiService.fetchData().then(data => console.log(data));
return <div>Loading...</div>;
}
// Внедрение зависимости
const apiService = container.resolve('apiService');
render(<UserComponent apiService={apiService} />, document.getElementById('root'));
Такой подход делает компонент полностью независимым от конкретной реализации сервиса.
Inferno поддерживает контекст (Inferno.createContext),
что позволяет передавать зависимости глубоко в дерево компонентов без
необходимости пробрасывать их через все уровни пропсов.
import { createContext } from 'inferno';
const ApiContext = createContext(null);
function App() {
const apiService = container.resolve('apiService');
return (
<ApiContext.Provider value={apiService}>
<UserComponent />
</ApiContext.Provider>
);
}
function UserComponent() {
return (
<ApiContext.Consumer>
{apiService => {
apiService.fetchData().then(data => console.log(data));
return <div>Loading...</div>;
}}
</ApiContext.Consumer>
);
}
Контекст идеально подходит для глобальных зависимостей, таких как сервисы API, менеджеры состояния или конфигурации.
Использование DI в Inferno позволяет построить гибкую архитектуру, где компоненты остаются независимыми, легко тестируемыми и адаптируемыми к изменениям в приложении.