Основы работы с хранилищем Vuex
Vuex является универсальным хранилищем данных для js фреймворка Vue. Использовать его стоит когда с одними данными должны работать разные компоненты. Чаще всего разработчик сам понимает когда это требуется.
Статья выполняет роль небольшой подсказки по функционалу Vuex. Для детального изучения рекомендуем обратиться к официальной документации. В примерах подразумевается что в проекте используется компонентный подход.
Установка может осуществляться несколькими способами:
npm install vuex --save // способ 1 yarn add vuex // способ 2
На практике часто ставят готовую сборку vue-cli с этим дополнением.
Далее в проекте создают каталог store с файлом index.js внутри. В нем можно создавать модель данных, но на практике в контейнере часто хранят данные разной направленности. Поэтому рекомедуется разбивать всё на модули. В этом случае index.js будет содержать примерно следующее:
import Vue from 'vue' import Vuex from 'vuex' import Auth from './modules/auth' import Chat from './modules/chat' Vue.use(Vuex) export default new Vuex.Store({ modules: { Auth, Chat } })
То есть, в папке store создается вложенный каталог modules, а в нём уже файлы с данными. В примере это файл auth.js и chat.js, где .js при импорте опускается.
Пустое хранилище модуля chat.js будет выглядеть так:
export default { state: { }, getters: { }, actions: { }, mutations: { } }
- State — содержит модель данных. Отличительно особенностью state в хранилище от data в комонентах является то, что их нельзя изменить напрямую. Для изменения обязательно нужно использовать функции из секции mutations.
- Getters — функции, которые возвращают какие-то данные из хранилища. К state можно обратиться и напрямую, но если перед этим требуется обработать или отфильтровать данные, то применяют именно getters. Например, в state хранятся сообщения чата, но в компоненты должны попадать эти же сообщения, только с заменой матерных слов на звездочки. В этом случае создается функция getters, которая в цикле обходит все сообщения, заменяет мат и возвращает уже обработанный массив.
- Mutations — синхронные функции для изменения состояния данных. Не могут быть асинхронными.
- Actions — блок с функциями, которые могут быть асинхронными. Для примера представьте что в хранилище есть массив с сообщениями для чата. От компонента приходит запрос на добавление нового сообщения. В этом случае мы могли бы вызвать мутацию и добавить его в state, но в 99% случаев перед этим нам необходимо сначала это сообщение добавить в базу данных на сервере и только после этого обновить состояние хранилища. Поэтому в actions создают функцию, которая будет сначала отправлять данные на сервер, ждать ответа и только затем вызывать мутацию.
Пример использования:
export default { state: { msg: ['msg 1', 'msg 2', 'msg 3'] }, getters: { get_msg(state){ return state.msg } }, actions: { add_new_msg(context, data){ // здесь можем добавлять новое сообщение в БД context.commit('add_msg', data) } }, mutations: { add_msg(state, data){ state.msg.push(data.msg) } } }
Взаимодействие компонентов с vuex
В каждом примере будет рассмотрено 2 подхода к работе с vuex. Обычный и с привязкой через map.
State
Для получения данных из хранилища следует создавать вычисляемые данные. Это касается как state, так и getters.
export default { name: 'chat', computed: { get_msg() { return this.$store.state.Chat.msg } }, }
Chat — наименование модуля хранилища. Если модульный подход для Vuex не используется, то пишется просто this.$store.state.msg
Теперь в компоненте можем вывести все сообщения из контейнера:
<ul> <li v-for="(msg, key) in get_msg" :key="key">{{msg}}</li> </ul>
Если в хранилище много полей, то использовать привязку данный из vuex к нашему компоненту так, будто это его собственные данные:
import { mapGetters } from 'vuex' export default { name: 'chat', computed: { get_msg() { return this.$store.state.Chat.msg } }, }
Getters
Вместо обращения напрямую к данным контейнера часто используют getters, которые сначала обрабатывают данные и только потом отдают.
export default { name: 'chat', computed: { get_msg() { return this.$store.getters.get_msg } }, }
А вот так автоматически привязывают нужные getters, что позволяет сократить код.
import { mapGetters } from 'vuex' export default { name: 'chat', computed: { ...mapGetters(['get_msg']) }, }
Mutations
Особенностью Vuex является невозможность изменения данных state напрямую. Для изменения обязательно нужно вызывать функции из секции mutations и вот как это делается:
export default { name: 'chat', methods: { add_msg_to_chat(){ this.$store.commit('add_msg', {msg: 'Hello world'}) } } }
Здесь мы вызывали мутацию add_msg и передали ей дополнительный параметр, который представляет из себя объект.
Есть и ещё один подход к работе с Vuex. Он заключается в привязке хранилища к компоненту так, будто они принадлежат самому компоненту:
import {mapMutations} from 'vuex' export default { name: 'chat', methods: { ...mapMutations(['add_msg']), add_msg_to_chat(){ this.add_msg({ msg: 'Hello world'}) } } }
Actions
Вызов действий по синтаксису похож на мутации, но стоит помнить что они могут быть асинхронны, а мутации — нет.
export default { name: 'chat', methods: { add_msg_to_chat(){ this.$store.dispatch('add_new_msg', {msg: 'Hello world'}) } } }
Все что изменилось, это commit поменялся на dispatch.
И второй подход:
import {mapActions, mapMutations} from 'vuex' export default { name: 'chat', methods: { ...mapMutations(['add_msg']), ...mapActions(['add_new_msg']), add_msg_to_chat(){ this.add_new_msg({ msg: 'Hello world'}) } } }
Если мутации не используются, то mapMutations можно удалить. В примере осталось для наглядности как использовать их совместно.