### Mutation
更改Vuex的store中的状态的**唯一方法是提交mutation**。Vuex中mutation非常类似于事件:
每个mutation都有一个字符串的**事件类型(type)**和一个**回调函数(handler)**。这个回调函数就是我们实际**进行状态更改的地方**,并且接受**state为第一个参数**。
```
const store = new Vuex.Store({
state:{ count : 1 },
mutations: {
increment ( state ) {
//变更状态
state.count++;
}
}
})
```
mutation handler不能直接调用,更像**事件注册**,“当触发一个类型为increment的mutation是,调用此函数。”
触发方法
```
store.commit('increment')
```
#### 提交荷载(Payload)
向`store.commit`传入额外的参数,即mutation的载荷(payload):
```
mutations:{
increment(state,n){
state.count+n
}
}
store.commit('increment', 10)
```
在大多数情况下,**载荷应该是一个对象**,这样可以包含多个字段并且记录的mutation户更易读
```
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
store.commit('increment', {
amount: 10
})
```
#### 对象风格的提交方式
另一种方式是直接使用包含type属性的对象:
```
store.commit({
type: 'increment',
amount: 10
})
```
当使用对象风格的提交方式,整个对象都作为载荷传给mutation函数,因此handler保持不变
```
mutations: {
increment ( state, payload){
state.ount+=payload.amount
}
}
```
#### Mutation 需遵守Vue的响应规则
Vuex的store中的状态是响应式的,当我们变更状态是,**监视状态的Vue组件也会自动更新**。
注意事项:
1. 最好提前在你的store中**初始化好所有所需属性**。
2. 当需要在对象上添加新属性时
* Vue.set(obj, 'newProp', 123)
* 已新对象替换老对象。state.obj={...state.obj,newProper:123}
* state.obj=JSON.parse(JSON.stringify(state.obj))
#### 使用常量替代Mutation事件类型
很常见的模式。四linter之类的工具发挥作用,同时把这些常量放在单独的文件中可以让代码的mutation一目了然。
```
mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
//store.js
import Vuex from 'vuex';
import {SOME_MUTATION } from './mutation-types'
const store = new Vuex.Store({
state:{...},
mutations:{
//我们可以使用ES2-15风格的计算属性命名功能更来使用一个常量作为函数名
[SOME_MUTATION] ( state ){
// mutate state
}
}
})
```
在需要多人协作的大型项目中,这会很有帮助。
#### Mutation必须是同步函数
重要原则:**mutation必须是同步函数**。
```
mutations: {
someMutation ( state ) => {
api.callAsynMethod( () => {
state.count++
})
}
}
```
当mutation触发的时候,回调函数还没有被调用,devtools不知道什么时候回调函数实际上被调用--实质上**任何在回到函数中进行的状态的改变都是不可追踪的**。
#### 在组件中提交Mutation
在组件中使用 `this.$store.commit( 'xxx' )` 提交mutation,或者使用mapMutation辅助函数将组件中的methods映射为 `store.commit`调用(需要在根节点注入store)
```
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')`
})
}
}
```
在mutation中混合异步调用会导致你的程序很难调试。在Vuex中,mutation都是同步事务:
```
store.commit('increment')
// 任何由 "increment" 导致的状态变更都应该在此刻完成。
```
#### 小实例
1. types.js
```
export const INCREMENT_COUNT = 'INCREMENT_COUNT';
export const DECREMENT_COUNT = 'DECREMENT_COUNT';
export const MULTIPLE_NUM = 'MULTIPLE_NUM';
```
2. mutations.js
```
import * as types from './types';
export const testMutation = {
[types.INCREMENT_COUNT](state){
return state.count++;
},
[types.DECREMENT_COUNT](state){
return state.count--;
},
[types.MULTIPLE_NUM](state, payload){
return state.count*=payload.num;
},
}
```
3.hello.js
```
import {testMutation} from '../mutations';
const state = {
count:1
}
export default {
state,
mutations: testMutation
}
```
4. store.js
```
import Vue from 'vue';
import Vuex from 'vuex';
import helloWorld from './modules/hello_world';
Vue.use(Vuex);
export default new Vuex.Store({
modules:{
hello:helloWorld,
}
})
```
5. main.js
```
import store from './vuex/store';
new Vue({
el: '#app',
store,
router,
components: { App },
template: '<App/>'
})
```
6. HelloWorld.vue
```
<button @click="increment">+++</button>{{count}}
<button @click="decrement">----</button>
<button @click="multiple()">*</button>
computed:{
...mapState({
count: state => state.hello.count,
}),
},
methods:{
...mapMutations({
increment:'INCREMENT_COUNT',//this.$store.commit('INCREMENT_COUNT')
decrement:'DECREMENT_COUNT',//this.$store.commit('DECREMENT_COUNT')
}),
multiple(){ this.$store.commit({type:'MULTIPLE_NUM',num:10})
}
}
```