Разделение логики: компоненты, composables, сервисы, сторы
↑ 8.2 Архитектура фронтенда и паттерны · ← Предыдущая · → Следующая
Зачем это на собесе
Главный практический вопрос: где что должно лежать во Vue-приложении.
Слои
| Слой | Ответственность | Знает о |
|---|---|---|
| Компонент | отображение, обработка пользовательского ввода | props, composables, store |
| Composable | переиспользуемая реактивная логика (UI и данные) | сервисы, другие composables |
| Store (Pinia) | общее состояние приложения, кэш данных | сервисы |
| Service / API | обмен с бэкендом, преобразование DTO | HTTP-клиент |
| Utils | чистые функции | ничего |
Как решать, где хранить
- локально нужное одному компоненту →
refв компоненте; - переиспользуемая логика с реактивностью → composable;
- разделяется между несколькими страницами или переживает навигацию → Pinia;
- данные с сервера с кэшем, повторной загрузкой, инвалидацией → TanStack Query (Vue Query);
- состояние в URL (фильтры, страница) → query-параметры роутера;
- форма → локально (vee-validate, FormKit).
Пример
// services/orders.ts
export const ordersApi = {
list: (params: OrdersQuery) => http.get<OrderDto[]>('/orders', { params }).then(r => r.data.map(toOrder)),
}
// composables/useOrders.ts
export function useOrders(query: Ref<OrdersQuery>) {
return useQuery({ queryKey: ['orders', query], queryFn: () => ordersApi.list(query.value) })
}<script setup lang="ts">
const filters = ref<OrdersQuery>({ page: 1 })
const { data: orders, isLoading } = useOrders(filters)
</script>
<template><OrdersTable :rows="orders" :loading="isLoading" /></template>Антипаттерны
- HTTP-запросы прямо в компонентах;
- вся логика в store, включая локальную;
- store как кэш серверных данных вручную (без инвалидации);
- «умные» компоненты, влезающие в глобальное состояние без нужды;
- события
emitчерез 5 уровней вместоprovide/injectили стора.
Тестируемость
Логика в composables и сервисах тестируется без DOM, компоненты — через Testing Library.
Вопросы с ответами
Когда данные класть в Pinia, а когда использовать Vue Query?
Серверные данные (кэш, refetch, инвалидация, состояние загрузки) — Vue Query. Клиентское глобальное состояние (пользователь, настройки, UI) — Pinia.
Чем composable отличается от store?
Composable создаёт независимое состояние на каждый вызов (если не вынесено наружу), store — единый общий экземпляр.