Полный цикл в digital

Работа с хранилищем Vuex в Vue

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

Основная причина использования Vuex — когда ваша структура данных становится настолько сложной, что её поддержка и передача между компонентами становится обременительной. Vuex предоставляет единую точку для хранения, обработки и получения данных, значительно упрощая процесс. Для небольших проектов или небольших независимых компонентов вам не обязательно использовать Vuex!

Зачем нужен Vuex

В Vue мы можем хранить состояние внутри объекта data() самой функции. Например, в приведённом ниже примере компонента, мы сохраняем состояние переключения внутри объекта data() как false, при щелчке по button будем менять значение на true:

App.vue<script>
    export default {
        data() {
            return {
                toggleState: false
            }
        },
        methods: {
            runToggle: function() {
                this.toggleState = true;
            }
        }
    }
</script>
<template>
    <button id="myButton" @click="runToggle">My Button</button>
<template>

Это отлично подходит для одново-двух действий, если у нас много различных компонентов и действий, все из которых зависят от одних и тех же данных, будут проблемы. В этом случае, для обработки этих данных можно использовать Vuex, который централизованно управляет всеми нашими данными, что позволяет легко манипулировать ими и получать к ним доступ.

Установка

Для начала Vuex нужно установить, это можно сделать через npm, выполнив команду:

npm install vuex@next

Начало работы

Когда Vuex установлен, мы можем приступить к его добавлению в наш проект. Для начала мы создадим одно центральное хранилище Vuex.

В проекте Vue есть файл src/main.js, давайте добавим туда наше хранилище. Вы можете обновить файл main.js, чтобы он выглядел так, как показано ниже:

src/main.jsimport { createApp } from 'vue'
// импортируем vuex
import { createStore } from 'vuex'
import App from './App.vue'
import router from './router'

const app = createApp(App);

// хранилище
const store = createStore({
    // данные хранилища
    state() {

    },
    // функции, которые возвращают какие-то данные из хранилища
    getters: {

    },
    // синхронные функции для изменения состояния данных
    mutations: {

    },
    // блок с функциями, которые могут быть асинхронными, например Ajax запрос
    actions: {

    }
});

// мы можем объединить функции use(), поэтому наше приложение теперь использует маршрутизатор и Vuex
app.use(router).use(store).mount('#app')

Хранилища Vuex легко настраивается, поскольку мы использовали при инициализации нашего приложения use(store), объект сразу же становятся доступны во всех частях проекта, для обращения к данным можно воспользоваться this.$store.state. Давайте рассмотрим, что делает каждый объект в нашем хранилище:

  1. state содержит модель данных. Отличительно особенностью state в хранилище от data в комонентах является то, что их нельзя изменить напрямую. Для изменения обязательно нужно использовать функции из секции mutations
  2. getters функции, которые возвращают какие-то данные из хранилища. К state можно обратиться и напрямую, но если перед этим требуется обработать или отфильтровать данные, то применяют именно getters. Например, в state хранятся сообщения чата, но в компоненты должны попадать эти же сообщения, только с заменой матерных слов на звездочки. В этом случае создается функция getters, которая в цикле обходит все сообщения, заменяет мат и возвращает уже обработанный массив
  3. mutations синхронные функции для изменения состояния данных. Не могут быть асинхронными
  4. actions блок с функциями, которые могут быть асинхронными. Для примера представьте что в хранилище есть массив с сообщениями для чата. От компонента приходит запрос на добавление нового сообщения. В этом случае мы могли бы вызвать мутацию и добавить его в state, но в 99% случаев перед этим нам необходимо сначала это сообщение добавить в базу данных на сервере и только после этого обновить состояние хранилища. Поэтому в actions создают функцию, которая будет сначала отправлять данные на сервер, ждать ответа и только затем вызывать мутацию

Состояние и геттеры

Как обсуждалось ранее, state будет хранить наши данные, а getters это методы, которые будут получать данные из нашего хранилища состояний.

Давайте рассмотрим пример хранилища. Ниже представлено хранилище состояний, возвращающее объект с именем users, представляющее собой массив различных значений:

src/main.jsconst store = createStore({
    state() {
        return {
            users: [
                {id: '1', name: 'Иван Иванов', email: 'test@yandex.ru'}
            ]
        }
    },
    getters: {
        users(state) {
            return state.users;
        }
    }
    mutations: {}
});

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

Мутация или изменение данных

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

src/main.jsmutations: {
    addUser(state, newUser)
    {
        if (newUser.id !== undefined && typeof newUser.name == 'string' && typeof newUser.email == 'string') {
            state.users.push({
                id: newUser.id,
                name: newUser.name,
                email: newUser.email
            })
        }
    }
}

Мы создали новый метод addUser, который принимает два аргумента: один — это состояние, то есть ссылка на хранилище состояний, а другой — данные, которые мы передаем с помощью этой мутации. Функция выше позволяет нам передать объект { id: '2', name: 'Иван Иванов', email: 'test@yandex.ru' } через мутацию в хранилище Vuex.

Обратите внимание, что все мутации синхронны. Если вы хотите использовать асинхронный код, вам придётся использовать actions

Асинхронный код

actions по сути очень похож на mutations, в том смысле, что они позволяют нам изменять данные в хранилище, но в actions выполняется асинхронный код:

src/main.jsconst store = createStore({
    state() {},
    getters: {},
    mutations: {},
    actions: {
        addUserAjax: function () {
            setTimeout(() => {
                // реальном коде здесь может быть Ajax
            }, 1000)
        }
    }
});

Как использовать мутации и геттеры

Полный код файла src/main.js:

src/main.jsimport {createApp} from 'vue'
import {createStore} from 'vuex'
import App from './App.vue'
import router from './router'

const app = createApp(App);

const store = createStore({
    state() {
        return {
            users: [
                {id: '1', name: 'Иван Иванов', email: 'test@yandex.ru'}
            ]
        }
    },
    getters: {
        users(state) {
            return state.users;
        }
    },
    mutations: {
        addUser(state, newUser) {
            if (newUser.id !== undefined && typeof newUser.name == 'string' && typeof newUser.email == 'string') {
                state.users.push({
                    id: newUser.id,
                    name: newUser.name,
                    email: newUser.email
                })
            }
        }
    },
    actions: {
        addUserAjax: function () {
            setTimeout(() => {
                // в реальном коде здесь может быть Ajax вызов
            }, 1000)
        }
    }
});

app.use(router).use(store).mount('#app')

Мы определили код в src/main.js, нам нужно использовать код в нашем приложении. Все функции доступны через this.$store, это достигается за счет инициализации хранилища Vuex в main.js.

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

App.vue<script>
export default {
  data() {
    return {
      // получаем длину массива
      id: this.$store.state.users.length,
    }
  },
  methods: {
    newUser: function () {
      // используем "commit" для вызова мутаций в Vuex
      this.$store.commit('addUser', {
        // увеличиваем id на единицу
        id: this.id++,
        // обращаемся к элементам через ref
        name: this.$refs.username.value,
        email: this.$refs.email.value
      })
      let allUsers = JSON.stringify(this.$store.getters.users);
      console.log('Добавлен новый пользователь!')
      console.log(`Все пользователи: ${allUsers}`);
    }
  }
};
</script>
<template>
  <div id="new-user">
    <input type="text" placeholder="Добавьте имя пользователя" id="username" ref="username">
    <input type="text" placeholder="Добавьте email" id="email" ref="email">
    <input type="submit" id="submit-user" @click="newUser" value="Submit">
  </div>
  <p>{{ this.$store.state }}</p>
</template>

Когда пользователь нажимает кнопку Отправить, мы вызываем нашу мутацию через:

this.$store.commit('addUser', {});

Это связано с тем, что мы не вызываем мутации напрямую, вместо этого мы используем commit для их вызова.

Для вызова асинхронных событий actions, необходимо вызвать this.$store.dispatch('actonName', {}), где первый аргумент это действие которое нужно вызвать, второй аргумент это данные которые нужно передать.

Мы также использовали наш геттер для вывода в консоль журнала всех добавляемых пользователей.

Заполните форму уже сегодня!
Для начала сотрудничества необходимо заполнить заявку или заказать обратный звонок. В ответ получите коммерческое предложение, которое будет содержать индивидуальную стратегию с учетом требований и поставленных задач
Работаем по будням с 9:00 до 18:00. Заявки, отправленные в выходные, обрабатываем в первый рабочий день до 12:00.
Спасибо, ваш запрос принят и будет обработан!