Контейнер зависимостей
@vue-modeler/dc — это контейнер зависимостей на основе shared composable.
Контейнер решает проблему управления жизненным циклом моделей и сервисов:
- Упрощает совместное использование моделей и сервисов между компонентами
- Отделяет бизнес-логику от представления
- Позволяет реализовать принципы MVVM, DDD, SOLID
Основные возможности
- ⚡ Ленивая загрузка: создает зависимости только когда они нужны
- 🗑️ Автоматическое удаление: удаляет неиспользуемые зависимости
- 🔧 Поддержка destructor: автоматически вызывает метод
destructorпри очистке - 💾 Постоянные экземпляры: позволяет создавать долгоживущие сервисы
TIP
Контейнер зависимостей хранит зависимости, НО не поддерживает автоматического внедрения (autowire). Разработчик самостоятельно внедряет зависимости в отдельном модуле или слое.
Как это работает?
Контейнер работает по принципу "создай по требованию, удали когда не нужно":
- Регистрация фабрики — вы регистрируете фабрику для создания экземпляра, получаете shared composable
- Создание экземпляра — экземпляр создается только при первом обращении
- Переиспользование — при повторных обращениях возвращается существующий экземпляр
- Отслеживание ссылок — контейнер считает, сколько компонентов используют экземпляр
- Очистка — когда счетчик использования становится 0, экземпляр удаляется
Регистрация фабрики
provider регистрирует фабрику зависимости и создает shared composable, который будет использоваться в компонентах.
Фабрика — простая функция, которая может возвращать значение любого типа.
Контейнер хранит то, что вернула фабрика. Никаких дополнительных действий не производит. Зависимости не внедряет.
import { provider } from '@vue-modeler/dc';
const useDependency = provider(() => {
// ваша фабрика по созданию экземпляра
return {
// экземпляр с методами и данными
};
});
// так тоже можно
const useSymbol = provider(() => new Symbol('dependency'));
const useNumber = provider(() => 10);
const useTrue = provider(() => true);
// передаем зависимости в конструктор
const useObject = provider(() => new SomeModel(
useDependency(),
useSymbol(),
useNumber(),
useTrue()
));Использование в компонентах
Пример использования провайдера внутри шаблона компонента:
<template>
<div>{{ model.state }}</div>
</template>
<script setup lang="ts">
import { useDependency } from '@/providers/myDependency';
const model = useObject(); // получаем экземпляр
</script>Постоянные экземпляры
Бывают случаи, когда нужно создать экземпляр, который будет оставаться в памяти приложения после использования. Например, сервисы уровня приложения, кэши или менеджеры состояния.
Для этого нужно передать опцию persistentInstance: true в функцию provider.
const usePersistentService = provider(
() => new MyService(),
{ persistentInstance: true } // дополнительные опции
);Основные особенности постоянных экземпляров:
- Сохраняются в контейнере даже после освобождения всей области видимости
- Сохраняют своё состояние между перезагрузками компонентов
- Вложенные провайдеры становятся постоянными автоматически, если находятся внутри постоянного провайдера
- Полезны для сервисов уровня приложения, кэшей и менеджеров состояния
Например, вот как выглядит использование вложенных провайдеров:
// вложенный провайдер становится постоянным вместе с основным
const useNestedService = provider(() => new NestedService());
const usePersistentService = provider(
() => new MainService(useNestedService()),
{ persistentInstance: true }
);WARNING
На клиенте используйте постоянные экземпляры осторожно, поскольку они не будут удаляться автоматически.
Для SSR постоянные экземпляры безопасны, так как при каждом запросе создается новый экземпляр контейнера, а старый удаляется вместе с содержимым.
