Как использовать Pinia в Vue 3

Управление состоянием — одно из требований при создании современного веб-сайта Vue 3. Такие функции, как аутентификация, элементы корзины, светлый/темный режим, должны быть доступны двум или более компонентам на веб-сайте vue, следовательно, необходимо управление состоянием. Цель этой статьи — помочь вам использовать Pinia для управления состоянием на современном веб-сайте Vue 3.

Что такое Pinia?

Pinia — это библиотека управления состоянием, предоставленная создателем vue для эффективного управления состоянием без шаблонного кода. Он предоставляет вам универсальное хранилище для доступа к состоянию и его изменения из любого компонента вашей кодовой базы.

Предварительные условия

Для работы с этим руководством необходимы базовые знания Javascript и API композиции vue3. И, конечно же, редактор кода (желательно Visual Studio Code).

Настройка Vue3 с помощью Pinia

Прежде всего откройте интерфейс командной строки, перейдите в предпочитаемый каталог папки и введите команду npm create vue@latest.

https://enterprisevue.dev/img/create-vue-3-8e4abfa-506.png

Это поможет вам создать кодовую базу vue с предпочитаемыми вами конфигурациями, выбрав «да» или «нет» при появлении подсказки. Назовите свой проект и нажмите Enter. В этой статье мы собираемся нажимать «Нет» на каждый вопрос, за исключением вопроса, показанного для добавления Pinia и vue router.

https://enterprisevue.dev/img/vue3-5b0d9ffe-596.png

После всех настроек введите следующие команды, чтобы запустить проект.

cd your-project-name
npm install
npm run dev

https://enterprisevue.dev/img/npm-ec63238-773.png

Вы должны увидеть страницу, подобную приведенной ниже, на localhost:5173

https://enterprisevue.dev/img/preview-d094a661-1366.png

Работа с Pinia

Шаг 1 (Создание Store)

Store — это место или контейнер, в котором хранится наше состояние. Доступ к этому хранилищу (или сторам) можно получить из любого места вашей кодовой базы (до этого мы еще доберемся).

Выполнив первоначальную настройку vue, удалите все шаблонные коды и файлы в папке компонентов и хранилища. Чтобы упростить задачу, мы собираемся создать простой веб-сайт без стиля, который отображает список товаров на одной странице и корзину, в которой хранятся товары, которые понравились пользователю, как на веб-сайте электронной коммерции.

Теперь внутри папки stores создайте файл с именем cart.js. Этот файл будет содержать и хранить объекты, которые пользователь добавляет в массив состояний (cartItems). В этом файле импортируйте defineStore из Pinia и ref из vue.

import { ref, computed} from 'vue'
import { defineStore} from 'pinia'

Это хранилище определений, которое мы только что импортировали, как следует из его названия, используется для определения или создания экземпляра хранилища. Этот метод сообщает vue и всей вашей кодовой базе, что функция, к которой он подключен, является хранилищем. Чтобы увидеть defineStore в действии, создайте и экспортируйте переменную (const) и присвойте ей defineStore.

export const = defineStore()

Но прежде чем дать имя этой только что созданной переменной, вам нужно кое-что знать о предпочтительном соглашении Pinia об именах store:

  • Имя переменной хранилища должно начинаться с use
  • Тогда имя хранилища должно следовать за использованием, т. е. именем файла, в котором вы создаете хранилище (в нашем случае store).
  • Наконец, имя должно заканчиваться на store.

Следуя приведенному выше соглашению об именах, имя нашей функции store будет называться useCartStore.

export const useCartStore = defineStore();

Важно знать, что defineStore принимает два аргумента:

  • Название store (строка)
  • Стрелочная функция

В качестве названия нашего store мы выберем cart (корзину).

export const useCartStore = defineStore("cart", () => {});

Помимо прочего, стрелочная функция должна возвращать объект. Этот объект будет содержать все, что вы создадите внутри функции стрелки.

export const useCartStore = defineStore("cart", () => {
  return {};
});

Шаг 2 (State, getters, actions)

State, getters и actions — это то, что находится внутри хранилища. Давайте узнаем, что они собой представляют.

State: это именованная константа, которой присвоен ref. Ссылка может быть строкой, массивом, объектом, логическим значением или нулевым значением. Обычно в каждом store есть только один. Если хранилище имеет более одного состояния, создайте для него другое хранилище.

Getters: это именованные константы, которые вычислены присвоены им. Они используются, когда вы хотите изменить исходное значение состояния перед доступом к нему.

Actions: это именованные функции, которые используются для изменения свойства состояния или выполнения действия через состояние или с его помощью.

Чтобы создать состояние, перейдите к стрелочной функции внутри defineStore и создайте константу. Назначьте метод ref созданной константе. В нашем случае нам нужен массив для хранения объектов.

Мы назовем этот массив carsItems. После создания состояния обязательно включите его в возвращаемый объект, чтобы к нему можно было получить глобальный доступ.

export const useCartStore = defineStore("cart", () => {
  const cartItems = ref([]);

  return { cartItems };
});

Давайте создадим геттер, который сортирует наш массив в порядке их добавления. Помните, для этого используется ключевое слово Computed:

export const useCartStore = defineStore("cart", () => {
  const cartItems = ref([]);

  //getter
  const sortItems = computed(() =>
    [...cartItems.value].sort((a, b) => b.id - a.id)
  );

  return { cartItems, sortItems };
});

Наконец, давайте создадим два действия; один для добавления объекта в состояние carsItems с помощью метода push, другой для удаления элемента, добавленного из состояния carsItems, с помощью метода фильтра.

export const useCartStore = defineStore("cart", () => {
  const cartItems = ref([]);

  //getter
  const sortItems = computed(() =>
    [...cartItems.value].sort((a, b) => b.id - a.id)
  );

  //actions
  const addItems = (item) => {
    //Find the object whose id is similar to the item
    const checkItems = cartItems.value.find((x) => x.id === item.id);
    if (!checkItems) {
      cartItems.value.push(item);
    }
  };
  const removeItems = (id) => {
    cartItems.value = cartItems.value.filter((x) => x.id !== id);
  };
  return { cartItems, sortedItems, addItems, removeItems };
});

Обратите внимание, что мы немного изменили действие addItems; он проверит, был ли добавлен объект, чтобы избежать его повторного добавления.

Шаг 3 (Интерфейс веб-сайта)

Это руководство будет неполным без пользовательского интерфейса для проверки нашего состояния и модификаторов. Итак, мы создадим две страницы с простым пользовательским интерфейсом и массивом из 10 объектов. В папке представлений создайте новый файл с именем CartView.vue.

https://enterprisevue.dev/img/websiteui-199a1e0c-230.png

Нажмите HomeView и создайте массив ссылок из 10 объектов. Сопоставьте эти объекты и отобразите их с помощью кнопки, каждая из которых имеет базовый стиль.

    <script setup>
    import { ref } from 'vue';

    const newItems = ref([
      {
        title: 'HP Laptop',
        id: 1
      },
      {
        title: 'Iphone X',
        id: 2
      },
      {
        title: 'Travelling Bag',
        id: 3
      },
      {
        title: 'Nike Shoes',
        id: 4
      },
      {
        title: 'Leather Belts',
        id: 5
      },
      {
        title: 'Mechanical Keyboard',
        id: 6
      },
      {
        title: 'Water Gun',
        id: 7
      },
      {
        title: 'Earth Globe',
        id: 8
      },
      {
        title: 'Javascript book',
        id: 9
      },
      {
        title: 'Black T-shirt',
        id: 10
      },
    ])
    </script>
    <template>
      <main>
        <h1>Items for sale</h1>
        <div v-for="item in newItems" :key="item.id">
          <div class="item">
            <h1>
              
            </h1>
            <button>Add</button>
          </div>
        </div>
      </main>
    </template>

    <style>
      .item {
        width: 50em;
        height: auto;
        margin: 10px auto;
        border: solid black 3px;
      }
      button {
        margin-left: 10px;
        height: 20px;
        width: 70px;
      }
    </style>

Затем щелкните файл CartView и создайте div с тегом h1.

<template>
  <div>
    <h1>This is the cartView page</h1>
  </div>
</template>

Теперь перейдите в папку маршрутизатора и откройте файл index.js. Обновите маршрут и измените его на корзину.

import { createRouter, createWebHistory } from "vue-router";
import HomeView from "../views/HomeView.vue";
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: "/",
      name: "home",
      component: HomeView,
    },
    {
      path: "/cart",
      name: "cart",
      component: () => import("../views/CartView.vue"),
    },
  ],
});
export default router;

Наконец, перейдите в файл App.vue и создайте новый RouterLink для страницы корзины.

    <script setup>
      import { RouterLink, RouterView} from 'vue-router'
    </script>
    <template>
      <header>
        <RouterLink to="/">
          <h2>Home</h2>
        </RouterLink>
        <RouterLink to="/cart">
          <h2>Cart</h2>
        </RouterLink>
      </header>
      <RouterView />
    </template>
    <style>
      header {
        display: flex;
      }
      header :first-child {
        margin-right: 10px;
      }
    </style>

Ваш пользовательский интерфейс должен выглядеть так:

https://enterprisevue.dev/img/uipreview-9322da5d-1352.png

https://enterprisevue.dev/img/cartpage-3092c25c-638.png

Шаг 4 (Доступ к хранилищу в компонентах Vue)

На следующем шаге мы получим доступ к store и его компонентам из компонентов HomeView и CartView. Откройте файл HomeView.vue и импортируйте useCartStore.

import { useCartStore } from "../stores/cart";

Теперь создайте константу с именем store и назначьте ей нашу функцию useCartStore.

const store = useCartStore();

Затем деструктурируйте нужные вам методы из хранилища. В данном случае нам понадобится только действие addItems.

const { addItems } = store;

Свяжите действие addItems с кнопкой, которая будет действовать при нажатии. Не забудьте передать выбранный элемент в качестве аргумента.

<button @click="addItems(item)">Add</button>

Затем перейдите к файлу CartView.vue и файлу useCartStore.

<script setup>import {useCartStore} from '../stores/cart'</script>

Создайте константу с именем store, которому вы назначите useCartStore, и деструктурируйте действие removeItem и метод получения sortedItems из хранилища.

const store = useCartStore();
const { removeItems, sortedItems } = store;

Поскольку массив carsItems, который мы будем отображать, постоянно меняется, нам понадобится метод Pinia под названием storeToRefs.

storeToRefs — это метод Pinia, который делает состояние реактивным.

Прямой доступ к массиву carsItems может вызвать проблемы с реактивностью (массив не будет обновляться, когда это должно произойти). Итак, storeToRef добавит реактивность состоянию.

Давайте посмотрим, как это работает: внутри того же файла импортируйте storeToRef из Pinia.

import { storeToRefs } from "pinia";

Деструктурируйте элемент carsItems из экземпляра storeToRef, который вы присвоите константе. Передайте переменную store в качестве аргумента

const { cartItems } = storeToRefs(store);

Теперь перейдите к тегу шаблона и сопоставьте массив carsItems, чтобы отобразить добавленные элементы с кнопкой удаления в цикле.

    <template>
      <h1>This is the cart page</h1>
      <main v-if="cartItems.length > 0">
        <div v-for="item in cartItems" :key="item.id" class="item">
          <h1></h1>
          <button>Remove</button>
        </div>
      </main>
      <main v-else>
        <h2>There is nothing in your cart</h2>
      </main>
    </template>

Из приведенного выше кода вы можете видеть, что мы создали условие, которое будет отображать тег h2, когда внутри массива carsItems ничего нет. Далее свяжите действие RemoveItem с кнопкой удаления, чтобы удалить элемент при нажатии на него. Обязательно передайте идентификатор элемента в качестве аргумента.

<button @click="removeItems(item.id)">Remove</button>

Вы захотите, чтобы элементы, которые вы недавно добавили, отображались первыми. Для этого замените carsItems на sortedItems в цикле v-for и деструктурируйте его из storeToRefs, чтобы сделать его реактивным.

    <script setup>
    import { useCartStore } from '../stores/cart'
    import { storeToRefs } from 'pinia'
    const store = useCartStore()
    const { removeItems } = store
    const { cartItems, sortedItems } = storeToRefs(store)
    </script>
    <template>
      <h1>This is the cart page</h1>
      <main v-if="sortedItems.length > 0">
        <div v-for="item in sortedItems" :key="item.id" class="item">
          <h1></h1>
          <button @click="removeItems(item.id)">Remove</button>
        </div>
      </main>
      <main v-else>
        <h2>There is nothing in your cart</h2>
      </main>
    </template>

Это руководство не будет полным, если мы не протестируем наши коды в пользовательском интерфейсе. Итак, зайдите в браузер, где работает localhost:5173 , и нажмите любую кнопку добавления.

https://enterprisevue.dev/img/addbutton-5638191c-416.png

Перейдите на страницу корзины. Вы должны увидеть добавленный вами элемент на странице.

https://enterprisevue.dev/img/removebutton-1c95381b-1121.png

Нажмите кнопку «Удалить», чтобы удалить элемент со страницы.

https://enterprisevue.dev/img/removebtn-ff5bcd35-309.pnghttps://enterprisevue.dev/img/removebutton-1c95381b-1121.png

Шаг 5 (Инструменты разработчика Pinia)

Инструменты разработчика Pinia предоставляются vue, чтобы сделать ваш опыт разработки менее напряженным и более интересным. Таким образом, вы можете протестировать свой store и модификаторы в консоли без необходимости использования пользовательского интерфейса. Чтобы получить доступ к инструментам разработчика Pinia, перейдите в интернет-магазин Chrome и найдите инструменты разработчика vue.js.

https://enterprisevue.dev/img/devtools-b57c7cf7-337.png

Если он у вас уже установлен, как и у нас, на кнопке отобразится «удалить из Chrome», иначе отобразится «Добавить в Chrome». Нажмите кнопку, чтобы загрузить его.

https://enterprisevue.dev/img/vuedevtool-28bfc22-1215.pnghttps://enterprisevue.dev/img/addtochrome-51527cf-287.png

Когда добавлены инструменты разработчика vue, для получения инструментов разработчика Pinia нет необходимости выполнять дальнейшие настройки. Инструменты разработчика Vue проверяют ваш веб-сайт vue и автоматически генерируют инструменты разработчика для любого пакета vue, установленного в вашей кодовой базе. Итак, откройте консоль, нажмите vue и перезагрузите веб-страницу. Инструменты разработчика Vue должны взять Pinia из вашего пакета и сгенерировать для него инструменты разработчика.

https://enterprisevue.dev/img/previewdevtool-7257712e-404.png

https://enterprisevue.dev/img/usedevtool-1f64b407-1280.png

На изображении выше вы можете видеть, что инструменты разработчика Pinia отображают ваше состояние и геттеры. Таким образом, с консоли вы можете добавлять новые значения, удалять их и с легкостью сбрасывать состояние.

Заключение

В ходе этой статьи мы обсудили концепцию управления состоянием с помощью Pinia, настройку состояния и модификаций, универсальный доступ к состоянию без ошибок и инструменты разработчика Pinia. Для дальнейшего изучения посетите документацию Pinia.