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

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

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

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

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

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

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

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

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

  • 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 (Pinia) для Vue-приложения

Pinia — официальный state manager для Vue 3. Заменила Vuex начиная с Vue 3.x: проще API, полная поддержка TypeScript без дополнительных настроек, работает с Composition API, поддерживает devtools из коробки.

Стор в Pinia — это defineStore, который возвращает хук. Никаких мутаций, никаких неймспейсов, никаких модулей — просто функция с реактивным состоянием.

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

Установка и настройка Pinia, создание сторов под задачи проекта, типизация, async-действия, персистентность, интеграция с Vue Router и Axios, devtools.

Установка

npm install pinia

Регистрация в приложении:

// main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const app = createApp(App)
app.use(createPinia())
app.mount('#app')

Options Store

Синтаксис, похожий на Vuex Options API:

import { defineStore } from 'pinia'

export const useCartStore = defineStore('cart', {
  state: () => ({
    items: [] as CartItem[],
    loading: false,
    error: null as string | 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,
  },

  actions: {
    addItem(product: Product) {
      const existing = this.items.find((i) => i.id === product.id)
      if (existing) {
        existing.quantity++
      } else {
        this.items.push({ ...product, quantity: 1 })
      }
    },

    removeItem(id: string) {
      this.items = this.items.filter((i) => i.id !== id)
    },

    async checkout() {
      this.loading = true
      this.error = null
      try {
        await api.post('/orders', { items: this.items })
        this.$reset()
      } catch (err) {
        this.error = err instanceof Error ? err.message : 'Ошибка'
      } finally {
        this.loading = false
      }
    },
  },
})

Setup Store (Composition API)

Более гибкий вариант — ближе к Composition API:

import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useAuthStore = defineStore('auth', () => {
  const user = ref<User | null>(null)
  const token = ref<string | null>(null)

  const isAuthenticated = computed(() => token.value !== null)

  const roles = computed(() => user.value?.roles ?? [])

  function hasRole(role: string) {
    return roles.value.includes(role)
  }

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

  function logout() {
    user.value = null
    token.value = null
    delete axios.defaults.headers.common['Authorization']
  }

  return { user, token, isAuthenticated, roles, hasRole, login, logout }
})

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

<script setup lang="ts">
import { useCartStore } from '@/stores/cart'
import { storeToRefs } from 'pinia'

const cart = useCartStore()

// storeToRefs сохраняет реактивность при деструктуризации
const { items, total, count, loading } = storeToRefs(cart)

// actions деструктурируем напрямую (они не теряют контекст)
const { addItem, removeItem, checkout } = cart
</script>

<template>
  <div>
    <p>Товаров: {{ count }}, Сумма: {{ total }} ₽</p>
    <button @click="checkout" :disabled="loading || cart.isEmpty">
      {{ loading ? 'Оформление...' : 'Оформить заказ' }}
    </button>
  </div>
</template>

Деструктуризация напрямую (const { items } = cart) потеряет реактивность — нужен storeToRefs.

Взаимодействие между сторами

export const useOrderStore = defineStore('orders', () => {
  const orders = ref<Order[]>([])

  async function createOrder() {
    // используем другой стор внутри
    const cart = useCartStore()
    const auth = useAuthStore()

    if (!auth.isAuthenticated) throw new Error('Необходима авторизация')

    const order = await api.post<Order>('/orders', {
      items: cart.items,
      userId: auth.user!.id,
    })

    orders.value.push(order)
    cart.$reset()
    return order
  }

  return { orders, createOrder }
})

Персистентность

npm install pinia-plugin-persistedstate
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
export const useAuthStore = defineStore('auth', {
  state: () => ({ user: null, token: null }),
  persist: {
    key: 'auth',
    storage: localStorage,
    paths: ['token'], // сохраняем только token
  },
})

Тестирование

import { setActivePinia, createPinia } from 'pinia'
import { useCartStore } from '@/stores/cart'

beforeEach(() => {
  setActivePinia(createPinia())
})

test('addItem увеличивает количество', () => {
  const cart = useCartStore()
  cart.addItem({ id: '1', name: 'Test', price: 200 })
  cart.addItem({ id: '1', name: 'Test', price: 200 })

  expect(cart.count).toBe(2)
  expect(cart.total).toBe(400)
})

test('checkout вызывает api и сбрасывает корзину', async () => {
  vi.spyOn(api, 'post').mockResolvedValue({})
  const cart = useCartStore()
  cart.addItem({ id: '1', name: 'Test', price: 100 })
  await cart.checkout()
  expect(cart.isEmpty).toBe(true)
})

Структура файлов

src/
  stores/
    auth.store.ts
    cart.store.ts
    ui.store.ts
    orders.store.ts
    index.ts        # реэкспорт

Что делаем

Устанавливаем и настраиваем Pinia, проектируем сторы под предметную область, подключаем персистентность для нужных данных, настраиваем интеграцию с axios (токен в headers), покрываем тестами с помощью Vitest.

Срок: 1–2 дня.