state.md 6.9 KB

Состояние

Единое дерево состояния

Vuex использует единое дерево состояния — таким образом, один-единственный объект содержит всё глобальное состояние приложения и служит "единственным источником истины". Кроме того, это значит, что обычно для каждого приложения существует только одно хранилище. Благодаря единому дереву можно легко найти нужную часть состояния или делать слепки всего состояния приложения для отладки.

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

Передача состояния Vuex в компоненты Vue

Так как же отобразить состояние хранилища в компонентах Vue? Поскольку хранилище Vuex реактивно, проще всего использовать вычисляемые свойства:

// создадим компонент-счётчик:
const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return store.state.count
    }
  }
}

Любые изменения store.state.count вызовут обновление вычисляемого свойства, которые инициируют соответствующие обновления в DOM.

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

Vuex предоставляет механизм "инъекции" хранилища всем потомкам компонента, у которого указана опция store (предварительно необходимо вызвать Vue.use(Vuex)):

const app = new Vue({
  el: '#app',
  // указываем хранилище в опции "store", что обеспечит
  // доступ к нему также и для всех дочерних компонентов
  store,
  components: { Counter },
  template: `
    <div class="app">
      <counter></counter>
    </div>
  `
})

Указывая опцию store для корневого экземпляра, мы обеспечиваем доступ к хранилищу всем дочерним компонентам в this.$store. Давайте обновим наш пример со счётчиком:

const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return this.$store.state.count
    }
  }
}

Вспомогательная функция mapState

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

// с полной сборкой можно использовать как Vuex.mapState
import { mapState } from 'vuex'

export default {
  // ...
  computed: mapState({
    // стрелочные функции позволяют писать код очень лаконично
    count: state => state.count,

    // передача строки 'count' эквивалентна записи `state => state.count`
    countAlias: 'count',

    // если требуется доступ и к локальному состоянию, нужно использовать традиционную функцию
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })
}

В простых случаях в mapState можно передать и просто массив строк:

computed: mapState([
  // проксирует через this.count доступ к store.state.count
  'count'
])

Оператор распространения объектов

Обратите внимание: mapState возвращает объект. Как же быть, если нам нужны и локальные вычисляемые свойства? Обычно в таких случаях приходилось использовать вспомогательные утилиты для слияния объектов, и передавать уже результат их работы в computed. Однако, применив оператор распространения объектов (находящегося в статусе stage-3 ECMAScript proposal) мы можем изрядно упростить запись:

computed: {
  localComputed () { /* ... */ },
  // результаты работы mapState будут добавлены в уже существующий объект
  ...mapState({
    // ...
  })
}

Компоненты всё ещё могут иметь локальное состояние

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