Налаштування State Management (Vuex) для Vue-додатку
Коли SPA на Vue розростається, стан додатку стає десятками взаємозалежних змінних. Мутації розкидані по компонентах, а відстежити, хто і коли змінив дані, неможливо без логу дій. Наприклад, у проекті з 50 компонентами та 200 змінними стану будь-яка зміна може викликати ланцюгову реакцію багів. Ми часто стикаємося з такими проектами та налаштовуємо для них Vuex — стандартний state management для Vue. Він забезпечує строгий однонапрямлений потік даних: мутувати стан можна лише через мутації, що гарантує трасованість змін. Підходить і для Vue 2, і для Vue 3 (версія 4). Vuex не єдиний варіант: є Pinia, але для проектів на Vue 2 та в legacy-коді Vuex залишається стандартом де-факто. Налаштування Vuex включає кілька етапів, і з ним ви отримуєте передбачуваний стан та історію змін. Повна типізація Vuex і модульна архітектура — ключ до масштабування.
Які проблеми вирішуємо
- Порушення однонапрямленого потоку. Прямі зміни
stateз компонентів — типова помилка, що веде до непередбачуваної поведінки. Vuex фіксує всі мутації, і за допомогою DevTools можна за секунду знайти джерело. - Відсутність модульності. Весь стан в одному файлі (понад 1000 рядків) — складно підтримувати. Розбиваємо на модулі за предметними областями (cart, auth, ui), кожен зі своїм контекстом.
- Налагодження без DevTools. Без часової шкали мутацій неможливо зрозуміти, що призвело до багу. Vue DevTools показують історію змін і стан на будь-який момент, що скорочує час пошуку помилки в 2-3 рази.
- Відсутність типізації. JavaScript-стор без TypeScript — часті опечатки в іменах геттерів та екшенів. Впроваджуємо typed store wrapper, що дає автодоповнення і прибирає до 60% runtime-помилок. Вартість налаштування окупається за рахунок скорочення часу налагодження.
Як ми це робимо
Використовуємо Vuex 4 для Vue 3 або vuex@3 для Vue 2. Стек: TypeScript, модулі з namespaced: true, vuex-persistedstate для збереження токена та теми, Jest для тестування. Кожен модуль описуємо як типізований клас з інтерфейсом стану, геттерами, мутаціями та екшенами.
Порівняння Vuex та Pinia (для розуміння контексту)
| Критерій | Vuex | Pinia |
|---|---|---|
| Типізація | Потребує обгортки | З коробки |
| DevTools | Повна підтримка | Повна підтримка |
| Мутації | Обов'язкові | Не потрібні |
| Сумісність | Vue 2 та Vue 3 | Тільки Vue 3 |
| Розмір | ~10 КБ | ~2 КБ |
Vuex залишається затребуваним для великих проектів на Vue 2 та міграцій. За нашими спостереженнями, він у 2–3 рази скорочує час на налагодження завдяки суворим правилам та DevTools. А економія бюджету на підтримку може сягати 40%.
Що ви отримаєте після налаштування
| Компонент | Результат |
|---|---|
| Архітектура стору | Модульна структура з 3–7 модулів з чіткими межами |
| Типізація | Повна типізація з автосправленням в IDE |
| Persisted state | Збереження токена та теми в localStorage |
| Тести | Покриття ключових модулів модульними тестами (Jest) |
| Документація | README з описом модулів та прикладами використання |
Як правильно структурувати модулі Vuex?
Кожен модуль — це ізольований контекст. Приклад модуля cart:
// store/modules/cart.ts import type { Module } from 'vuex' import type { RootState } from '../types' interface CartState { items: CartItem[] loading: boolean } export const cart: Module<CartState, RootState> = { namespaced: true, state: () => ({ items: [], loading: false, }), getters: { total: (state) => state.items.reduce((sum, i) => sum + i.price * i.quantity, 0), }, mutations: { ADD_ITEM(state, product: Product) { const existing = state.items.find(i => i.id === product.id) if (existing) { existing.quantity++ } else { state.items.push({ ...product, quantity: 1 }) } }, CLEAR_CART(state) { state.items = [] }, }, actions: { async checkout({ commit, state }) { commit('SET_LOADING', true) await api.post('/orders', { items: state.items }) commit('CLEAR_CART') commit('SET_LOADING', false) }, }, } Чому варто використовувати typed store wrapper?
Для повної типізації створюємо обгортку useStore:
// store/typed-store.ts import { useStore as baseUseStore, Store } from 'vuex' import type { InjectionKey } from 'vue' import type { RootState } from './types' export const key: InjectionKey<Store<RootState>> = Symbol() export function useStore(): Store<RootState> { return baseUseStore(key) } Підключаємо в main.ts і використовуємо в компонентах:
import { useStore } from '@/store/typed-store' const store = useStore() // повністю типізований Завдяки typed store wrapper автодоповнення працює в будь-якому компоненті, а помилки типів відловлюються на етапі компіляції.
Як тестувати модулі Vuex?
Перевіряємо ключові сценарії: додавання товару, збільшення кількості, оформлення замовлення.
import { createStore } from 'vuex' import { cart } from '@/store/modules/cart' test('додавання товару збільшує лічильник', () => { const store = createStore({ modules: { cart } }) store.dispatch('cart/addItem', { id: '1', price: 100 }) expect(store.getters['cart/count']).toBe(1) }) Такий підхід гарантує, що зміни в сторі не ламають логіку роботи.
Приклад реального кейсу
На одному з проектів (інтернет-магазин на Vue 2) ми зіткнулися з тим, що стан корзини був розмазаний по 12 компонентах. Після впровадження модуля cart з persisted state час пошуку помилок скоротився в 3 рази, а кодова база зменшилася на 15%.Процес роботи
- Аналіз — вивчаємо поточний код, виявляємо проблемні місця.
- Проектування — визначаємо модулі, типи, інтерфейси.
- Реалізація — пишемо стор, типізацію, persisted state.
- Інтеграція — підключаємо до компонентів, замінюємо локальний стан.
- Тестування — покриваємо модульні тести та інтеграцію.
- Деплой — перевіряємо в продакшені, фікс багів.
Терміни орієнтовно — від 2 до 5 днів залежно від обсягу legacy-коду. Вартість розраховується індивідуально. Ми займаємося розробкою на Vue більше 5 років і реалізували понад 30 проектів з Vuex, тому знаємо всі підводні камені. Отримайте консультацію з налаштування Vuex для вашого проекту — ми оцінимо код і запропонуємо оптимальну архітектуру. Замовте налаштування під ключ — ми гарантуємо прозору історію змін і легку підтримку.
Типові помилки при налаштуванні Vuex
Не забувайте namespaced: true — без нього модулі конфліктують. Типізуйте мутації, використовуючи константи або TypeScript enum. Не зберігайте в стані дані, які можна обчислити — застосовуйте гетери. Не викликайте мутації з компонентів безпосередньо — тільки через екшени. Ці прості правила прибирають до 70% багів на старті.







