目录
- 只能通过 commit mutation 来修改 state 中的状态,如 this.$store.commit('事件名', 传入的额外参数可选)
- mutation 类似事件,每个 mutation 都有一个字符串的事件类型 type 和一个回调函数 handler
- mutation 里面定义的事件,接收 state 作为它的第一个参数
- mutation 提交载荷方式可以向 this.$store.commit() 传入额外的参数,多数情况下载荷是一个对象
- mutation 对象风格的提交方式,直接使用包含
type
属性的对象- mutation 必须是同步函数,方便调试有利于跟踪状态
mutations 介绍
- 更改 Vuex store 状态唯一方法是提交 commit mutation
- Vuex 中的 mutation 非常类似事件:每个 mutation 都有一个字符串的事件类型 (type) 和 一个回调函数 (handler)
- 这个回调函数是我们实际进行状态更改的地方,并且它接受 state 作为第一个参数
import Vue from "vue"
import Vuex from "vuex"
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
state.count++ // 变更状态
}
}
})
- 不能直接调用 mutation handler 这个选项
- 它更像事件注册:当触发一个类型为 increment 的 mutation 时,调用此函数
- 要唤醒一个 mutation handler,需要以相应的 type 调用 this.$store.commit 方法
this.$store.commit('increment')
提交载荷(Payload)
- 可以向 this.$store.commit() 传入额外的参数,即 mutation 的载荷(payload)
export default {
mutations: {
increment (state, n) {
state.count += n
}
}
}
this.$store.commit('increment', 10)
- 多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录 mutation 会更易读
export default {
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
}
this.$store.commit('increment', {
amount: 10
})
mutation 对象风格提交方式
- 提交 mutation 的另一种方式是直接使用包含
type
属性的对象
this.$store.commit({
type: 'increment',
amount: 10
})
- 当使用对象风格提交方式,整个对象都作为载荷传给 mutation 函数,因此 handler 保持不变
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
Mutation 需遵守 Vue 的响应规则
既然 Vuex store 中的状态是响应式的,那么当我们变更状态时,监视状态的 Vue 组件也会自动更新。这也意味着 Vuex 中的 mutation 也需要与使用 Vue 一样遵守一些注意事项:
- 最好提前在 store 中初始化所有所需属性
- 当需要在对象上添加新属性时,使用
Vue.set(obj, 'newProp', 123)
或者以新对象替换老对象。例如利用对象展开运算符
state.obj = { ...state.obj, newProp: 123 }
使用常量替代 Mutation 事件类型
- 使用常量替代 mutation 事件类型在各种 Flux 实现中是很常见的模式
- 可以使 linter 之类的工具发挥作用,同时把这些常量放在单独的文件中可以让你的代码合作者对整个 app 包含的 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: {
// 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
[SOME_MUTATION] (state) {
// mutate state
}
}
})
- 用不用常量取决于你,在多人协作大型项目中,会很有帮助。如果不喜欢,完全可以不这样做
Mutation 必须是同步函数
- 一条重要的原则就是要记住 mutation 必须是同步函数。为什么?请参考下面的例子:
mutations: {
someMutation (state) {
api.callAsyncMethod(() => {
state.count++
})
}
}
现在想象,我们正在 debug 一个 app 并且观察 devtool 中的 mutation 日志。每一条 mutation 被记录,devtools 都需要捕捉到前一状态和后一状态的快照。然而,在上面例子中 mutation 中的异步函数中的回调让这不可能完成:因为当 mutation 触发的时候,回调函数还没有被调用,devtools 不知道什么时候回调函数实际上被调用——实质上任何在回调函数中进行状态的改变都是不可追踪的。
在组件中提交 Mutation
你可以在组件中使用 this.$store.commit('xxx')
提交 mutation,或者使用 mapMutations
辅助函数将组件中的 methods 映射为 this.$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 中混合异步调用会导致你的程序很难调试。例如,当你调用了两个包含异步回调的 mutation 来改变状态,你怎么知道什么时候回调和哪个先回调呢?这就是为什么我们要区分这两个概念。在 Vuex 中,mutation 都是同步事务
this.$store.commit('increment')
// 任何由 "increment" 导致的状态变更都应该在此刻完成。
异步操作使用 Action
Vuex 案例实现递增递减功能
- 页面代码
<template>
<section>
{{count}}
<el-button @click="reduce">-</el-button>
<el-button @click="plus">+</el-button>
</section>
</template>
<script>
import { mapMutations, mapState } from "vuex";
export default({
computed: {
...mapState({
count: state => state.count
})
},
methods: {
...mapMutations({
decrease: 'decrease', // 递减 this.decrease 映射为 this.$store.commit('decrease')
increase: 'increase', // 递增
}),
reduce(){
this.decrease({
amount: 1
})
},
plus(){
this.increase({
amount: 1
})
}
}
})
</script>
- store/index.ts 代码
import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations'
Vue.use(Vuex)
export default new Vuex.Store({
state,
mutations,
})
- store/state.ts 代码
export default {
count: 1
}
- store/mutations.ts 代码
export default {
// 递减
decrease(state: any, payload: any){
state.count -= payload.amount
},
// 递增
increase(state: any, payload: any){
state.count += payload.amount
},
}
- devtools 跟踪的是 mutations 中的状态