Pinia: хранилище
Pinia — официальная библиотека для общего состояния во Vue. Хранилище (store) — объект с данными и функциями, который доступен любому компоненту без props и событий. В блоге список статей нужен и главной странице, и разделу управления, и счётчику в шапке — его удобно держать в одном хранилище.
Установка
npm install piniaПодключите Pinia в src/main.js:
// src/main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.mount('#app')Если при создании проекта отметить пункт Pinia, create-vue сделает это сам и положит пример хранилища в src/stores/counter.js.
Описание хранилища
Хранилища кладут в папку src/stores/, по одному файлу на тему. Создайте src/stores/posts.js:
// src/stores/posts.js
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const usePostsStore = defineStore('posts', () => {
const posts = ref([
{ id: 1, title: 'Привет, Vue', body: 'Первая статья блога.' },
{ id: 2, title: 'Компоненты', body: 'Как разбить страницу на части.' },
])
const count = computed(() => posts.value.length)
return { posts, count }
})defineStore принимает уникальное имя хранилища и функцию, которая устроена как <script setup>: ref — это данные хранилища (state), computed — вычисляемые значения (getters). Всё, что нужно снаружи, функция возвращает. Имя функции по соглашению — use + имя + Store.
Использование в компонентах
Вызовите usePostsStore() в любом компоненте. Например, в шапке src/App.vue:
<!-- src/App.vue -->
<script setup>
import { usePostsStore } from './stores/posts.js'
const postsStore = usePostsStore()
</script>
<template>
<nav>
<RouterLink to="/">Главная</RouterLink> |
<RouterLink to="/admin">Управление</RouterLink>
<span>Статей: {{ postsStore.count }}</span>
</nav>
<RouterView />
</template>И в src/views/AdminPostsView.vue:
<!-- src/views/AdminPostsView.vue -->
<script setup>
import { usePostsStore } from '../stores/posts.js'
const postsStore = usePostsStore()
</script>
<template>
<h2>Все статьи</h2>
<ul>
<li v-for="post in postsStore.posts" :key="post.id">{{ post.title }}</li>
</ul>
</template>В шапке видно «Статей: 2», а в разделе управления — две статьи. Оба компонента получили один и тот же объект: если статья добавится, обновятся оба места.
Инструменты разработчика
Расширение Vue DevTools для браузера показывает вкладку Pinia: там видно все хранилища и их текущие данные, и значения можно менять прямо там.
Частая ошибка
Деструктурировать хранилище: const { posts, count } = usePostsStore(). Хранилище — реактивный объект, и деструктуризация копирует текущие значения, после чего связь теряется: счётчик перестанет обновляться. Обращайтесь через точку — postsStore.count, а если деструктуризация нужна, используйте storeToRefs из следующего урока.