Ну что ж, пришло время поговорить о глобальном управлении состоянием в Vue-приложениях. Если вы работали с Vue 2, то наверняка помните о Vuex — официальной библиотеке для управления состоянием. Однако с Vue 3 появился новый инструмент — Pinia, который пришёл на смену Vuex и стал более лёгким, удобным и мощным.
С Pinia вы можете легко управлять состоянием вашего приложения, делая его организованным и масштабируемым. Сегодня мы разберёмся, как настроить и использовать Pinia для управления состоянием, и почему это гораздо проще, чем кажется.
Что такое Pinia?
Pinia — это официальный хранилище для Vue 3, заменяющее Vuex. Оно проще в использовании, не теряя при этом функциональности. Pinia обеспечивает реактивность состояния, модульность и позволяет легко работать с TypeScript.
Если в двух словах, Pinia позволяет вам хранить и управлять глобальными данными, которые могут быть доступны в любом месте приложения. Это решает проблему, когда данные передаются через множество компонентов или когда нужно делиться состоянием между разными частями приложения.
Установка Pinia
Как всегда, начнём с установки. Если вы используете Vite, процесс установки довольно прост:
npm install pinia
Теперь нужно подключить Pinia к вашему приложению. Откройте файл main.js и добавьте Pinia в цепочку использования вашего приложения:
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
const app = createApp(App);
// Создаем экземпляр Pinia
const pinia = createPinia();
// Подключаем его к приложению
app.use(pinia);
app.mount('#app');
Вот и всё! Pinia готова к использованию в вашем приложении.
Создание первого хранилища
Теперь давайте создадим хранилище, или, как его называют в Pinia, store. Хранилище — это некий глобальный объект, который может содержать состояние, геттеры (аналог computed), действия и мутации (для изменения состояния).
Создадим файл store/counter.js, который будет представлять наше первое хранилище:
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
// Начальное состояние
state: () => ({
count: 0
}),
// Геттеры — аналог computed
getters: {
doubleCount: (state) => state.count * 2
},
// Действия — аналог методов
actions: {
increment() {
this.count++;
},
decrement() {
this.count--;
}
}
});
Здесь мы создаём хранилище counter с помощью функции defineStore. Внутри определяем:
- State — начальное состояние. В нашем случае это просто счётчик count, который равен 0.
- Getters — это свойства, которые вычисляются на основе состояния. Например, doubleCount возвращает удвоенное значение счётчика.
- Actions — методы, которые могут изменять состояние. Например, метод increment увеличивает счётчик, а decrement — уменьшает.
Использование хранилища в компонентах
Теперь давайте посмотрим, как использовать хранилище в компонентах. Откроем какой-нибудь компонент, например App.vue, и подключим наше хранилище counter.
<template>
<div>
<h1>Счётчик: {{ counter.count }}</h1>
<h2>Удвоенное значение: {{ counter.doubleCount }}</h2>
<button @click="counter.increment">Увеличить</button>
<button @click="counter.decrement">Уменьшить</button>
</div>
</template>
<script>
import { useCounterStore } from './store/counter';
export default {
setup() {
// Подключаем хранилище
const counter = useCounterStore();
return { counter };
}
};
</script>
Здесь всё просто: мы импортируем наше хранилище useCounterStore и подключаем его внутри функции setup. Теперь мы можем напрямую обращаться к состоянию (counter.count), геттерам (counter.doubleCount) и методам (counter.increment, counter.decrement).
Pinia автоматически делает все свойства реактивными, так что любое изменение состояния будет сразу отражаться в интерфейсе.
Модульность в Pinia
Что здорово в Pinia — это модульность. Если в вашем приложении есть несколько независимых частей, например, управление пользователями и товары в магазине, вы можете легко разделить их по разным хранилищам.
Например, создадим ещё одно хранилище для управления пользователями:
import { defineStore } from 'pinia';
export const useUserStore = defineStore('user', {
state: () => ({
user: null
}),
actions: {
login(userData) {
this.user = userData;
},
logout() {
this.user = null;
}
}
});
Теперь у вас есть отдельное хранилище для работы с пользователями, которое можно использовать независимо от хранилища counter. Модульный подход делает структуру более понятной и легко поддерживаемой.
Асинхронные действия
Pinia прекрасно поддерживает асинхронные действия. Допустим, вы хотите логиниться через API. Это можно сделать с использованием async внутри вашего хранилища.
Пример асинхронного действия для входа:
actions: {
async login(username, password) {
try {
const user = await api.login(username, password);
this.user = user;
} catch (error) {
console.error('Ошибка при входе:', error);
}
}
}
Pinia автоматически отслеживает все изменения, которые происходят в асинхронных действиях, так что вам не нужно беспокоиться о синхронизации данных.
Работа с Pinia и TypeScript
Если вы работаете с TypeScript, то Pinia — просто находка! Она полностью совместима с TS и позволяет вам создавать типизированные хранилища, что значительно упрощает работу в больших проектах.
Пример типизированного хранилища:
import { defineStore } from 'pinia';
interface State {
count: number;
}
export const useCounterStore = defineStore<'counter', State>({
state: () => ({
count: 0
}),
actions: {
increment() {
this.count++;
}
}
});
Благодаря этому вы получаете все преимущества автодополнения и проверки типов, что минимизирует ошибки и упрощает разработку.
Почему Pinia лучше Vuex?
Pinia пришла на смену Vuex, и на это есть ряд причин:
- Простота: Меньше бойлерплейта, чем в Vuex. Не нужно вручную создавать мутации.
- Совместимость с Composition API: Pinia легко интегрируется с Vue 3 и его новыми возможностями.
- TypeScript: Отличная поддержка типов из коробки.
- Модульность: Простое разделение состояния на отдельные модули.
- Асинхронность: Легкость работы с асинхронными действиями.
В целом, Pinia — это новый стандарт для управления состоянием в Vue-приложениях, и его стоит использовать, если вы работаете с Vue 3.