# Управление состоянием приложения

# Официальная Flux-подобная библиотека

Большие приложения часто могут усложняться из-за множества фрагментов состояния, разбросанных по многим компонентам, и взаимодействий между ними. Для решения этой проблемы Vue предлагает Vuex (opens new window) — собственную библиотеку для управления состоянием, вдохновлённую языком Elm. Она интегрируется с vue-devtools (opens new window) и позволяет заниматься отладкой с функцией «машиной времени» (opens new window).

# Информация для React-разработчиков

При переходе на Vue с React, может быть интересно как Vuex сравнивается с Redux (opens new window), самой популярной реализацией Flux в этой экосистеме. Redux не зависит от слоя представления, поэтому его можно легко использовать и с Vue с помощью простых привязок (opens new window). Vuex отличается тем, что он знает, что находится в приложении Vue. Это позволяет ему лучше интегрироваться и предлагать более интуитивно понятный API, улучшая процесс разработки.

# Создание простого контейнера состояния с нуля

Часто упускается из виду, что «источником истины» в приложениях Vue будет реактивный объект data — экземпляр компонента лишь проксируют доступ к нему. Поэтому, если есть часть состояния, которая должна быть общей для нескольких экземпляров, то можно воспользоваться методом reactive, чтобы сделать объект реактивным:

const { createApp, reactive } = Vue

const sourceOfTruth = reactive({
  message: 'Привет'
})

const appA = createApp({
  data() {
    return sourceOfTruth
  }
}).mount('#app-a')

const appB = createApp({
  data() {
    return sourceOfTruth
  }
}).mount('#app-b')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div id="app-a">Приложение A: {{ message }}</div>

<div id="app-b">Приложение B: {{ message }}</div>
1
2
3

Сейчас при изменении sourceOfTruth оба приложения appA и appВ будут автоматически обновлять свои представления. Теперь есть единый источник истины, но отладка станет кошмаром. Любая часть данных может быть изменена в любой части приложения в любое время, не оставляя никаких следов.

const appB = createApp({
  data() {
    return sourceOfTruth
  },
  mounted() {
    sourceOfTruth.message = 'Пока' // оба приложения теперь показывают 'Пока'
  }
}).mount('#app-b')
1
2
3
4
5
6
7
8

Для решения этой проблемы можно адаптировать шаблон проектирования «состояние»:

const store = {
  debug: true,

  state: reactive({
    message: 'Привет!'
  }),

  setMessageAction(newValue) {
    if (this.debug) {
      console.log('setMessageAction вызван с', newValue)
    }

    this.state.message = newValue
  },

  clearMessageAction() {
    if (this.debug) {
      console.log('clearMessageAction вызван')
    }

    this.state.message = ''
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

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

При этом в каждом экземпляре/компоненте может быть и своё собственное состояние, которым он управляет:

<div id="app-a">{{ sharedState.message }}</div>

<div id="app-b">{{ sharedState.message }}</div>
1
2
3
const appA = createApp({
  data() {
    return {
      privateState: {},
      sharedState: store.state
    }
  },
  mounted() {
    store.setMessageAction('Пока!')
  }
}).mount('#app-a')

const appB = createApp({
  data() {
    return {
      privateState: {},
      sharedState: store.state
    }
  }
}).mount('#app-b')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

Управление состоянием

Совет

Не стоит заменять исходный объект состояния в своих действиях — компоненты и хранилище должны ссылаться на один и тот же объект для отслеживания мутаций.

Если продолжить развивать концепцию, в которой компонентам не разрешается напрямую изменять состояние хранилища, а вместо этого они должны отправлять события, которые уведомляют хранилище о необходимости выполнения каких-то действий, то в конечном итоге приходим к архитектуре Flux (opens new window). Преимущество этой архитектуры заключается в том, что можно записывать все мутации состояния, происходящие с хранилищем, и создавать продвинутые средства для отладки, такие как журналы мутаций, моментальные снимки и путешествие по истории изменений / «машину времени».

Это вновь возвращает к Vuex (opens new window), поэтому если дочитали до этого момента — пожалуй, пришло время попробовать!