Настройка State Management (Vuex) для Vue-приложения

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Настройка State Management (Vuex) для Vue-приложения
Средняя
от 1 рабочего дня до 3 рабочих дней
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Настройка State Management (Vuex) для Vue-приложения

Vuex — стандартный state manager для Vue 2 и ранних проектов на Vue 3. Основан на flux-архитектуре: state, getters, mutations, actions, modules. Мутировать состояние можно только через mutations, что гарантирует трассируемость изменений.

Если проект уже использует Vuex или работает на Vue 2 — настраиваем и оптимизируем именно его. Для новых проектов на Vue 3 предпочтительнее Pinia, но Vuex 4 полностью совместим с Vue 3.

Что входит в работу

Установка Vuex 4, проектирование модулей, типизация стора через TypeScript, async actions, namespace-модули, helpers, DevTools, тестирование.

Установка

# Vue 3
npm install vuex@4

# Vue 2
npm install vuex@3
// main.ts (Vue 3)
import { createApp } from 'vue'
import store from './store'

createApp(App).use(store).mount('#app')

Типизированный стор — корневой модуль

// store/types.ts
export interface RootState {
  version: string
}

// store/index.ts
import { createStore } from 'vuex'
import type { RootState } from './types'
import { cart } from './modules/cart'
import { auth } from './modules/auth'

export default createStore<RootState>({
  state: {
    version: '1.0.0',
  },
  modules: {
    cart,
    auth,
  },
})

Модуль cart

// store/modules/cart.ts
import type { Module } from 'vuex'
import type { RootState } from '../types'

interface CartState {
  items: CartItem[]
  loading: boolean
  error: string | null
}

export const cart: Module<CartState, RootState> = {
  namespaced: true,

  state: () => ({
    items: [],
    loading: false,
    error: null,
  }),

  getters: {
    total: (state) =>
      state.items.reduce((sum, i) => sum + i.price * i.quantity, 0),

    count: (state) =>
      state.items.reduce((sum, i) => sum + i.quantity, 0),

    isEmpty: (state) => state.items.length === 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 })
      }
    },

    REMOVE_ITEM(state, id: string) {
      state.items = state.items.filter((i) => i.id !== id)
    },

    CLEAR_CART(state) {
      state.items = []
    },

    SET_LOADING(state, loading: boolean) {
      state.loading = loading
    },

    SET_ERROR(state, error: string | null) {
      state.error = error
    },
  },

  actions: {
    addItem({ commit }, product: Product) {
      commit('ADD_ITEM', product)
    },

    async checkout({ commit, state }) {
      commit('SET_LOADING', true)
      commit('SET_ERROR', null)
      try {
        await api.post('/orders', { items: state.items })
        commit('CLEAR_CART')
      } catch (err) {
        commit('SET_ERROR', err instanceof Error ? err.message : 'Ошибка оформления')
      } finally {
        commit('SET_LOADING', false)
      }
    },
  },
}

Модуль auth с Vue Router guard

// store/modules/auth.ts
export const auth: Module<AuthState, RootState> = {
  namespaced: true,

  state: () => ({
    user: null,
    token: localStorage.getItem('token'),
  }),

  getters: {
    isAuthenticated: (state) => !!state.token,
    currentUser: (state) => state.user,
    hasRole: (state) => (role: string) => state.user?.roles?.includes(role) ?? false,
  },

  mutations: {
    SET_AUTH(state, { user, token }: { user: User; token: string }) {
      state.user = user
      state.token = token
      localStorage.setItem('token', token)
    },
    CLEAR_AUTH(state) {
      state.user = null
      state.token = null
      localStorage.removeItem('token')
    },
  },

  actions: {
    async login({ commit }, credentials: LoginCredentials) {
      const { data } = await axios.post<LoginResponse>('/api/auth/login', credentials)
      commit('SET_AUTH', { user: data.user, token: data.token })
      axios.defaults.headers.common['Authorization'] = `Bearer ${data.token}`
    },

    logout({ commit }) {
      commit('CLEAR_AUTH')
      delete axios.defaults.headers.common['Authorization']
    },

    async fetchCurrentUser({ commit, state }) {
      if (!state.token) return
      const { data } = await axios.get<User>('/api/me')
      commit('SET_AUTH', { user: data, token: state.token })
    },
  },
}

Использование в компонентах

Composition API (Vue 3 + Vuex 4)

import { computed } from 'vue'
import { useStore } from 'vuex'

export default defineComponent({
  setup() {
    const store = useStore()

    const total = computed(() => store.getters['cart/total'])
    const isLoading = computed(() => store.state.cart.loading)

    function addItem(product: Product) {
      store.dispatch('cart/addItem', product)
    }

    function checkout() {
      store.dispatch('cart/checkout')
    }

    return { total, isLoading, addItem, checkout }
  },
})

Options API + helpers

import { mapState, mapGetters, mapActions } from 'vuex'

export default {
  computed: {
    ...mapState('cart', ['items', 'loading']),
    ...mapGetters('cart', ['total', 'count', 'isEmpty']),
    ...mapGetters('auth', ['isAuthenticated']),
  },
  methods: {
    ...mapActions('cart', ['addItem', 'checkout']),
    ...mapActions('auth', ['logout']),
  },
}

Типизация через typed store wrapper

Vuex 4 + TypeScript — болезненная комбинация без обёртки. Один из рабочих подходов:

// store/typed-store.ts
import { useStore as baseUseStore, Store } from 'vuex'
import type { InjectionKey } from 'vue'
import type { RootState } from './types'

export type AppStore = Store<RootState>

export const key: InjectionKey<AppStore> = Symbol()

export function useStore(): AppStore {
  return baseUseStore(key)
}
// main.ts
import { key } from './store/typed-store'
app.use(store, key)

// в компоненте
import { useStore } from '@/store/typed-store'
const store = useStore() // типизированный

Плагин для персистентности

npm install vuex-persistedstate
import createPersistedState from 'vuex-persistedstate'

export default createStore({
  plugins: [
    createPersistedState({
      paths: ['auth.token', 'ui.theme'],
      storage: window.localStorage,
    }),
  ],
  // ...
})

Тестирование модулей

import { createStore } from 'vuex'
import { cart } from '@/store/modules/cart'

function buildStore() {
  return createStore({ modules: { cart } })
}

test('ADD_ITEM увеличивает quantity для существующего товара', () => {
  const store = buildStore()
  const product = { id: '1', name: 'Test', price: 100 }

  store.dispatch('cart/addItem', product)
  store.dispatch('cart/addItem', product)

  expect(store.getters['cart/count']).toBe(2)
  expect(store.getters['cart/total']).toBe(200)
})

Структура проекта

src/
  store/
    index.ts              # createStore + modules
    types.ts              # RootState и общие типы
    typed-store.ts        # типизированный useStore
    modules/
      auth.ts
      cart.ts
      ui.ts
      notifications.ts

Что делаем

Настраиваем структуру модулей под предметную область, включаем namespace везде, добавляем TypeScript-типизацию через typed store wrapper, настраиваем persistedstate для нужных данных, покрываем тестами ключевые модули, подключаем Vue DevTools.

Срок: 2–3 дня, включая рефакторинг существующего кода при наличии.