Согласно словарю, Х-фактор — это «качество, которое невозможно описать, которое делает кого-то особенным». Vuex, вероятно, наоборот. Vuex предназначен для управления потоком внутри приложения и делает его максимально простым для понимания, даже несмотря на то, что у него есть собственный X-фактор. Vuex — это библиотека, которая реализует шаблон управления состоянием для приложений Vue.js. Как шаблон управления состоянием, состояние может быть изменено только предсказуемым образом. В этом блоге я опишу простоту Vuex и сделаю X-фактор простым.

Что в магазине

Хранилище — это объект, который содержит состояние и все функции, необходимые приложению для получения значений состояния и его изменения. Изменение состояния всегда будет синхронным. В магазине пять основных частей:

Состояние

Государство является единственным источником истины. Это глобальный объект, который определяет конкретное состояние всего приложения. Каждый компонент имеет производное состояние из этого единственного источника. Любое изменение в этом производном состоянии повлияет на глобальное состояние и наоборот.

Как мы его используем?

  • Во-первых, Vuex будет объявлен в индексном файле:
//index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
  • Во-вторых, объект состояния будет объявлен в объекте хранилища:
//index.js
const store = new Vuex.Store({
 state: {
   streamText: '',
   constTextsArr:
     [
       { title: 'store and more', footer: '@shmargadt' },
       { title: 'Vuex', footer: '@tomers' }
     ]
 }
})
  • В-третьих, производное состояние от глобального состояния будет использоваться для соответствующего компонента:
//Stream.js
export default {
 template: `<div>{{ stream }}</div>`,
 computed: {
   stream() {
     return this.$store.state.streamText
   }
 }
}

Добытчики

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

Как мы его используем?

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

//index.js
const store = new Vuex.Store({
 ...
 getters: {
   streamLength: state => {
     return state.streamText.length
   },
   titles: state => {
     return state.constTextsArr.filter(constTexts => constTexts.title)
   }
 }
 ...
})

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

//Stream.js
export default {
 template: `<div>{{ stream }}. Text length: {{ streamLength }}</div>`,
 computed: {
   stream() {
     return this.$store.state.streamText
   },
   streamLength() {
     return this.$store.state.streamText.length
   }
 }
}

«Вы должны быть той переменой, которую хотите видеть в мире». Махатма Ганди

Мутации

Как сказал Махатма Ганди, если вы хотите изменить состояние, вы должны написать мутацию. Мутации — это чистые функции, которые принимают состояние в качестве параметра с дополнительными параметрами или без них и возвращают новое состояние без каких-либо побочных эффектов. Vuex обновит новое состояние как глобальное состояние.

Как мы его используем?

Мутации будут объявлены в объекте хранилища.

//index.js
const store = new Vuex.Store({
 ...
 mutations: {
   addStreamText (state) {
     const radix = 36;
     const startAfterPointIndex = 2;
     const constEndIndex = 5;
     state.streamText +=    Math.random().toString(radix).substring(startAfterPointIndex, constEndIndex);
   }
 }
})

Мутации используют зарезервированное слово commit, которое применяет мутацию.

store.commit('addStreamText')

Подход в стиле фиксации — это объектно-ориентированный подход. В этом подходе мутация получает объект в качестве входных данных. Свойства объекта будут «Тип» и «Полезная нагрузка».

//index.js
const store = new Vuex.Store({
 ...
 mutations: {
   addStreamText (state, payload) {
     state.streamText +=  payload.streamTextAsObjectStyleCommit;
   }
 }
})
const radix = 36;
const startAfterPointIndex = 2;
const constEndIndex = 5;
const streamTextAsObjectStyleCommit =  Math.random().toString(radix).substring(startAfterPointIndex, constEndIndex);
store.commit({ type: 'addStreamText', streamText: streamTextAsObjectStyleCommit })

Мутации — лучшая практика в синхронных операциях, но как насчет асинхронных операций? как это можно сделать?

"Асинхронное действие!"
"тук-тук"
"Кто там?"

Действия

Действия — это асинхронная операция. Эта функциональность охватывает все виды операций, которые необходимы приложению. Каждое действие может фиксировать изменения состояния при получении ответа асинхронной операции или отправлять другое действие при его получении.

Как мы его используем?

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

//index.js
import { Logger } from './services/logger'
…
const store = new Vuex.Store({
 ...
 actions: {
   addStreamText ({ commit, state }) {
     // the Streamer API accepts a success callback and a failure callback
     Streamer.getStreamText(
       //params for the API
       state.streamText.length,
       // handle success
       (resp) => commit({type: 'addStreamText', streamTextAsObjectStyleCommit: resp.data.streamText}),
       // handle failure
       (resp) => Logger.error('getStreamText from server failed', resp.data.errorType)
     )
   }
 }
})
store.dispatch('addStreamText')

Отправка действия будет запущена внутри компонента:

import { mapActions } from 'vuex'
export default {
 template: `<div><div>{{ stream }}. Text length: {{ streamLength }}</div> <button> </button></div>`,
 computed: {
   stream() {
     return this.$store.state.streamText
   },
   streamLength() {
     return this.$store.state.streamText.length
   }
 },
 methods: {
   ...mapActions([
     'addStreamText', // map `this.addStreamText()` to `this.$store.dispatch('addStreamText')`
   ])
 }
}

Модули — не кладите все яйца в одну корзину

Когда наше приложение становится больше, состояние в основном увеличивается вместе с ним. Чтобы наш магазин и весь его контекст оставались читаемыми, мы разобьем наше состояние на небольшие модули. Каждый модуль будет содержать свой контекст — состояние, геттеры, мутации действий. Глобальное состояние будет состоять из состояний всех модулей.

Как мы его используем?

//index.js
const modulestreamText = {
 state: {
   streamText: ''
 },
 mutations: {    
   addStreamText (state, payload) { ….}
  },
 actions: {    
   addStreamText ({ commit, state }) {
  ….
   }
 },
 getters: {    
   streamLength: state => {
  ....
   }
 }
}
const moduleConstTexts = {
 state: {
   constTextsArr: ...
 },
 getters: {
   titles: state => {...}
 }
}
const store = new Vuex.Store({
 modules: {
   streamText: modulestreamText,
   ConstTexts: moduleConstTexts
 }
})
store.state.streamText // -> `modulestreamText`'s state
store.state.ConstTexts // -> `moduleConstTexts`'s state

«Это было необычно. К сожалению, необычайно плохо. “

- Саймон Коуэлл, судья The X Factor

Vuex — необычный паттерн, необычайно хороший. Реализация шаблона потока путем объявления состояния и его изменения синхронизированным образом делает поток приложения простым, поддерживаемым и односторонним. Как сказал Дэн Абрамов: «Библиотеки Flux похожи на очки: вы поймете, когда они вам понадобятся». Vuex ждет, когда вы наденете его очки, начните носить их прямо сейчас.