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

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

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

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

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

# Простой контейнер состояния с нуля

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

const { createApp, reactive } = Vue

const sourceOfTruth = reactive({
  message: 'Hello'
})

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">App A: {{ message }}</div>

<div id="app-b">App B: {{ message }}</div>
1
2
3

Теперь при любых изменениях sourceOfTruth обновится и appA, и appВ. Эффект «единого источника истины» достигнут, но отладка превратится в сущее мучение: любая часть данных может быть изменена любой частью приложения в любой момент и без каких-либо следов.

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

Для решения этой проблемы, мы можем использовать простое хранилище:

const store = {
  debug: true,

  state: reactive({
    message: 'Hello!'
  }),

  setMessageAction(newValue) {
    if (this.debug) {
      console.log('setMessageAction triggered with', newValue)
    }

    this.state.message = newValue
  },

  clearMessageAction() {
    if (this.debug) {
      console.log('clearMessageAction triggered')
    }

    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('Goodbye!')
  }
}).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), так что если вы дочитали до этого места — пожалуй, пора его попробовать!

Deployed on Netlify.
Последнее обновление: 2021-01-18, 21:06:34 UTC