Архитектура
Pinia для state management
Контекст
Нужно решение для управления глобальным состоянием приложения.
Решение
Использовать Pinia как единственное решение для state management.
Обоснование
| Критерий | Pinia |
|---|---|
| Vue 3 | Нативная поддержка |
| TypeScript | First-class |
| DevTools | Отличная интеграция |
| Composition API | Нативная |
| Bundle size | Маленький |
| Nuxt | Модуль @pinia/nuxt |
Настройка
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@pinia/nuxt'],
})
Паттерн store (Setup Store)
// app/stores/user.ts
export const useUserStore = defineStore('user', () => {
// State
const user = ref<User | null>(null)
const token = ref<string | null>(null)
// Getters
const isAuthenticated = computed(() => !!token.value)
const fullName = computed(() =>
user.value ? `${user.value.firstName} ${user.value.lastName}` : ''
)
// Actions
async function login(credentials: LoginCredentials) {
const config = useRuntimeConfig()
// Login — публичный endpoint, используем $fetch
const response = await $fetch<LoginResponse>('/auth/login', {
baseURL: config.public.apiBase,
method: 'POST',
body: credentials,
})
token.value = response.token
user.value = response.user
localStorage.setItem('token', response.token)
}
function logout() {
token.value = null
user.value = null
localStorage.removeItem('token')
navigateTo('/login')
}
// Init (SPA — читаем из localStorage при старте)
function init() {
const savedToken = localStorage.getItem('token')
if (savedToken) {
token.value = savedToken
// fetchCurrentUser()
}
}
return {
user,
token,
isAuthenticated,
fullName,
login,
logout,
init,
}
})
Использование
<script setup lang="ts">
import { storeToRefs } from 'pinia'
const userStore = useUserStore()
const { user, isAuthenticated } = storeToRefs(userStore)
function handleLogout() {
userStore.logout()
}
</script>
Правила
1. Используй storeToRefs для реактивности
// ❌ Потеря реактивности
const { user } = userStore
// ✅ Сохранение реактивности
const { user } = storeToRefs(userStore)
2. Не мутируй state напрямую
// ❌ Плохо
userStore.user.name = 'New Name'
// ✅ Хорошо
userStore.updateProfile({ name: 'New Name' })
3. Init в plugin (SPA)
// app/plugins/init.client.ts
export default defineNuxtPlugin(() => {
const userStore = useUserStore()
userStore.init()
})
Последствия
Положительные
- Простой API
- Отличная TypeScript поддержка
- Auto-imports в Nuxt
Отрицательные
- Нужна дисциплина при мутациях