Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

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 из следующего урока.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.