Установка и настройка Pinia

Pinia — современная библиотека управления состоянием для Vue.js, пришедшая на смену Vuex. Она обеспечивает реактивное управление данными, модульность и простоту интеграции с Vue 3. Основные преимущества Pinia — легкость в использовании, поддержка TypeScript и строгая типизация, а также оптимизация для Composition API.


Установка Pinia

Для установки Pinia используется менеджер пакетов npm или yarn:

npm install pinia

или

yarn add pinia

После установки необходимо интегрировать Pinia в проект Vue. В основном файле приложения (main.js или main.ts) подключение выглядит следующим образом:

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

const app = createApp(App);
const pinia = createPinia();

app.use(pinia);
app.mount('#app');

Здесь создается экземпляр Pinia и регистрируется в приложении через метод use(). Этот шаг обязателен, иначе магазины Pinia не смогут быть использованы в компонентах.


Создание первого магазина

Магазин Pinia — это объект, который управляет состоянием, геттерами и действиями. Для его создания используется функция defineStore.

Пример базового магазина:

// stores/counter.js
import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0
  }),
  getters: {
    doubleCount: (state) => state.count * 2
  },
  actions: {
    increment() {
      this.count++;
    },
    decrement() {
      this.count--;
    }
  }
});

Ключевые моменты:

  • defineStore('имя', {...}) — создает магазин с уникальным идентификатором.
  • state — функция, возвращающая объект состояния.
  • getters — вычисляемые свойства, которые автоматически реактивны.
  • actions — методы для изменения состояния. В отличие от Vuex, здесь нет необходимости использовать мутации.

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

После создания магазина его можно использовать в компонентах через Composition API:

<template>
  <div>
    <p>Count: {{ counter.count }}</p>
    <p>Double Count: {{ counter.doubleCount }}</p>
    <button @click="counter.increment">Increment</button>
    <button @click="counter.decrement">Decrement</button>
  </div>
</template>

<script>
import { useCounterStore } from '../stores/counter';

export default {
  setup() {
    const counter = useCounterStore();
    return { counter };
  }
}
</script>

Важно: каждый вызов useCounterStore() возвращает тот же экземпляр магазина, если Pinia подключена к приложению. Это гарантирует единое состояние во всех компонентах.


Подключение Pinia к Vue Router

Pinia отлично интегрируется с Vue Router. Магазины можно использовать для управления глобальным состоянием маршрутов или авторизации. Например:

import { createRouter, createWebHistory } from 'vue-router';
import { useAuthStore } from './stores/auth';
import Home from './views/Home.vue';
import Login from './views/Login.vue';

const routes = [
  { path: '/', component: Home, meta: { requiresAuth: true } },
  { path: '/login', component: Login }
];

const router = createRouter({
  history: createWebHistory(),
  routes
});

router.beforeEach((to, from, next) => {
  const authStore = useAuthStore();
  if (to.meta.requiresAuth && !authStore.isLoggedIn) {
    next('/login');
  } else {
    next();
  }
});

export default router;

Такой подход позволяет держать логику авторизации централизованной в Pinia, не дублируя её в компонентах.


Плагины и расширения

Pinia поддерживает подключение плагинов для расширения функциональности:

  • Persisted state — сохранение состояния магазина в localStorage или sessionStorage.
  • Logger — логирование действий и изменений состояния для отладки.

Пример подключения persisted state:

import { createPinia } from 'pinia';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';

const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);

После подключения любой магазин можно настроить на автоматическое сохранение:

export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  persist: true
});

Рекомендации по структуре проекта

Для удобства и масштабируемости рекомендуется:

  • Разделять магазины по функциональности (stores/counter.js, stores/auth.js).
  • Использовать Composition API для доступа к магазинам.
  • Включать строгую типизацию через TypeScript, чтобы исключить ошибки при работе с состоянием.
  • Минимизировать побочные эффекты в actions — они должны изменять только состояние магазина.

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

Pinia удобно использовать для хранения данных, получаемых с API. Пример асинхронного действия:

export const useTodoStore = defineStore('todo', {
  state: () => ({ todos: [] }),
  actions: {
    async fetchTodos() {
      const response = await fetch('https://jsonplaceholder.typicode.com/todos');
      this.todos = await response.json();
    }
  }
});

Асинхронные действия выполняются напрямую в actions, без необходимости создавать отдельные модули или middleware, что упрощает архитектуру приложения.


Pinia сочетает простоту и мощь, позволяя создавать масштабируемые и легко поддерживаемые приложения на Vue.js. Правильная организация магазинов, использование геттеров и действий обеспечивает чистый, реактивный и предсказуемый поток данных.