Отметим: когда 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% багов на старте.







