# Мутации
Единственным способом изменения состояния хранилища во Vuex являются мутации. Мутации во Vuex очень похожи на события: каждая мутация имеет строковый **тип** и **функцию-обработчик**. В этом обработчике и происходят, собственно, изменения состояния, переданного в функцию первым аргументом: ```js const store = new Vuex.Store({ state: { count: 1 }, mutations: { increment(state) { // изменяем состояние state.count++; } } }); ``` Вызывать функцию-обработчик напрямую — нельзя. Это больше похоже на обработку события: "Когда мутация типа `increment` инициирована, вызывается этот обработчик". Чтобы инициировать обработку мутации, необходимо вызвать `store.commit`, указав её тип: ```js store.commit('increment'); ``` ### Мутации с нагрузкой При вызове `store.commit` в мутацию можно также передать дополнительный параметр, называемый **нагрузкой (`payload`)**: ```js // ... mutations: { increment (state, n) { state.count += n } } ``` ```js store.commit('increment', 10); ``` В большинстве случаев нагрузка будет объектом, содержащим несколько полей. Запись мутаций в таком случае становится более описательной: ```js // ... mutations: { increment (state, payload) { state.count += payload.amount } } ``` ```js store.commit('increment', { amount: 10 }); ``` ### Объектный синтаксис Другой способ вызвать мутацию — это передать в commit единственный параметр, в котором `type` указан напрямую: ```js store.commit({ type: 'increment', amount: 10 }); ``` При использовании объектной записи, объект передаётся в качестве нагрузки целиком, так что обработчик остаётся тем же самым: ```js mutations: { increment (state, payload) { state.count += payload.amount } } ``` ### Мутации следуют правилам реактивности Vue Поскольку состояние хранилища Vuex — это реактивная переменная Vue, при возникновении мутации зависящие от этого состояния компоненты Vue обновляются автоматически. Кроме того, это значит, что мутации Vuex имеют те же самые подводные камни, что и реактивность в обычном Vue: 1. Лучше инициализировать изначальное состояние хранилища, указав все поля в самом начале. 2. При добавлении новых свойств объекту необходимо либо: * Использовать `Vue.set(obj, 'newProp', 123)`, или * Целиком заменить старый объект новым. Например, используя [синтаксис расширения объектов](https://github.com/tc39/proposal-object-rest-spread) можно написать так: ```js state.obj = { ...state.obj, newProp: 123 }; ``` ### Использование констант для обозначения типов мутаций В различных вариантах реализации Flux этот подход используется весьма часто. Вынесите все константы с типами мутаций и действий в отдельный файл, чтобы было проще использовать линтеры и другие инструменты, а также чтобы дать читателям возможность с первого взгляда понять, какие мутации возможны в приложении: ```js // mutation-types.js export const SOME_MUTATION = 'SOME_MUTATION'; ``` ```js // store.js import Vuex from 'vuex' import { SOME_MUTATION } from './mutation-types' const store = new Vuex.Store({ state: { ... }, mutations: { // "вычисляемые имена" из ES2015 позволяют использовать // константу в качестве имени функции [SOME_MUTATION] (state) { // здесь будет изменяться состояние } } }) ``` Тем не менее, использовать константы для указания типов мутаций совершенно необязательно, хотя это и может оказаться полезным в крупных проектах. ### Мутации должны быть синхронными Нужно помнить одно важное правило: **обработчики мутаций обязаны быть синхронными**. Почему? Рассмотрим пример: ```js mutations: { someMutation (state) { api.callAsyncMethod(() => { state.count++ }) } } ``` Теперь представьте, что вы отлаживаете приложение и смотрите в лог мутаций в инструментах разработчика. Для каждой залогированной мутации devtools должен сохранить слепки состояния приложения "до" и "после" её наступления. Однако, асинхронный коллбэк внутри приведённой выше мутации делает это невозможным: мутация-то уже записана, и у devtools нет никакой возможности знать, что далее будет вызван коллбэк, а, значит, и инициируемые им изменения становится, по сути дела, невозможно отследить. ### Вызов мутаций в компонентах Мутации можно вызывать из кода компонентов, используя `this.$store.commit('xxx')`, или применяя вспомогательный метод `mapMutations`, который проксирует вызовы `store.commit` через методы компонентов (для этого требуется наличие корневой ссылки на хранилище `$store`): ```js import { mapMutations } from 'vuex'; export default { // ... methods: { ...mapMutations([ 'increment', // `this.increment()` будет вызывать `this.$store.commit('increment')` // mapMutations также поддерживает нагрузку: 'incrementBy' // `this.incrementBy(amount)` будет вызывать `this.$store.commit('incrementBy', amount)` ]), ...mapMutations({ add: 'increment' // `this.add()` будет вызывать `this.$store.commit('increment')` }) } }; ``` ### О действиях Привнесение асинхронности в мутации могло бы изрядно затруднить понимание логики программы. Например, если вызываются два метода, оба с асинхронными коллбэками, изменяющими состояние приложения — как предсказать, какой из коллбэков будет вызван первым? Именно поэтому концепции изменений и асинхронности рассматриваются по отдельности. Во Vuex **мутации — это синхронные транзакции**: ```js store.commit('increment'); // все изменения состояния, вызываемые мутацией "increment", // к этому моменту уже должны произойти. ``` Для обработки асинхронных операций существуют [Действия](actions.md).