Vue.js - 使用Vuex进行状态管理详解4(mutations同步修改状态)
作者:hangge | 2026-10-03 09:09
在前面的文章中,我介绍了 Vuex 中 state 状态的定义与读取,以及 getters 计算属性的使用。但我们还没有涉及如何修改 store 中的状态。在 Vuex 中,修改状态的唯一方式是提交 mutation。mutations 类似于事件注册,每个 mutation 都有一个字符串类型和一个回调函数,回调函数就是实际进行状态更改的地方。本文将详细介绍 mutations 的基本使用、参数传递、常量类型定义以及 mapMutations 辅助函数的使用技巧。



一、基本介绍
1,什么是 mutations
(1)更改 Vuex 的 store 中状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的事件类型(type)和一个回调函数(handler)。
(2)这个回调函数就是我们实际进行状态更改的地方,并且它会接收 state 作为第一个参数。
注意:我们不能直接调用一个 mutation 处理函数。这个选项更像是事件注册:当触发一个类型为 increment 的 mutation 时,调用此函数。要唤醒一个 mutation 处理函数,我们需要以相应的 type 调用 store.commit 方法。
2,mutations 必须是同步函数
(1)一条重要的原则:在 Vuex 中,mutations 必须是同步函数。因为 Vue.js devtools 工具会记录 mutations 函数的日志,每条 mutation 被记录时,devtools 都需要捕捉到前一次状态和后一次状态的快照。
(2)如果在 mutation 中执行异步操作,就无法追踪到数据的变化,这与 mutation 的设计初心相悖。
提示:如果需要执行异步操作来修改状态,应该使用 actions(将在下一篇文章中介绍)。actions 可以包含任意异步操作,然后通过提交 mutation 来修改状态。
二、mutations 基本使用
1,定义 mutations
下面通过一个计数器案例来演示 mutations 的基本使用。修改 src/store/index.js 文件,在 mutations 中定义了两个函数:increment 和 decrement。这两个函数不能直接调用,因为 mutations 中定义的选项更像事件注册。
import { createStore } from 'vuex'
const store = createStore({
state() {
return {
counter: 0
}
},
// 定义 mutations
mutations: {
// 定义 increment 函数,可获取 state 对象
increment(state) {
state.counter++
},
// 定义 decrement 函数
decrement(state) {
state.counter--
}
}
})
export default store
2,提交 mutation
(1)要调用 mutations 中定义的函数,需要使用 $store.commit 方法提交一个类型。下面是我们组件代码:
- 可以看到,当单击 "+1" 按钮时,会调用 $store.commit('increment') 方法提交一个类型为 increment 的 mutation 函数,即上面 mutations 中定义的 increment 函数。
<template>
<div>
<h4>当前计数:{{ $store.state.counter }}</h4>
<button @click="$store.commit('increment')">+1</button>
<button @click="$store.commit('decrement')">-1</button>
</div>
</template>
<script>
export default {}
</script>
(2)保存代码后,在浏览器中显示的效果如下,计数器可以正常使用:

三、mutations 接收参数
1,payload 参数说明
(1)在 mutations 中定义的函数可以接收两个参数:
- 第一个参数:state 对象,可以访问和修改 store 中定义的状态
- 第二个参数:payload(载荷),用于接收提交时传递过来的参数
提示:在大多数情况下,payload 应该是一个对象,这样可以包含多个字段,并且记录的 mutation 会更易读。
2,使用示例
(1)继续完善上面的计数器案例,添加一个 "+10" 的功能。修改 store/index.js 文件:
import { createStore } from 'vuex'
const store = createStore({
state() {
return {
counter: 0,
}
},
mutations: {
increment(state) {
state.counter++
},
decrement(state) {
state.counter--
},
// 定义 incrementN 函数,接收 payload 参数
incrementN(state, payload) {
// payload 通常是对象类型
state.counter += payload.num
},
},
})
export default store
(2)接着修改组件,添加一个 "+10" 按钮:
<template>
<div>
<h4>当前计数:{{ $store.state.counter }}</h4>
<button @click="$store.commit('increment')">+1</button>
<button @click="$store.commit('decrement')">-1</button>
<button @click="addTen">+10</button>
</div>
</template>
<script>
export default {
methods: {
addTen() {
// 向 mutation 的 payload 参数传递一个对象
this.$store.commit('incrementN', { num: 10, name: 'hangge', age: 18 })
}
}
}
</script>
(3)保存代码后,在浏览器中显示的效果如下,"+10" 功能可以正常使用:
3,对象风格的提交方式
$store.commit 还支持对象风格的提交方式,将 type 和其他参数放在同一个对象中。
提示:当使用对象风格的提交方式时,整个对象都作为载荷传给 mutation 函数。
// 方式一:普通的提交方式
this.$store.commit('incrementN', { num: 10, name: 'hangge', age: 18 })
// 方式二:对象风格的提交方式
this.$store.commit({
type: 'incrementN', // 指定 mutation 类型
num: 10,
name: 'hangge',
age: 18
})
四、mutations 常量类型
1,为什么需要常量类型
(1)从上面的案例中可以看出,为了触发 mutations 中的回调函数,$store.commit 提交的 type 值必须与 mutations 中定义的函数名称相同。
(2)为了确保 type 值与函数名称一一对应,我们通常会将 type 值提取为一个常量,这样可以避免在工作中出现不必要的错误。
提示:使用常量替代 mutation 事件类型在各种 Flux 实现中是很常见的模式。这样可以使 linter 之类的工具发挥作用,同时把这些常量放在单独的文件中可以让你的代码合作者对整个 app 包含的 mutation 一目了然。
2,使用示例
(1)新建 src/store/mutation-types.js 文件,定义常量:
export const INCREMENT_N = 'incrementN'
(2)修改 store/index.js 文件,使用常量作为函数名:
import { createStore } from 'vuex'
import { INCREMENT_N } from './mutation-types'
const store = createStore({
state() {
return {
counter: 0
}
},
mutations: {
increment(state) {
state.counter++
},
decrement(state) {
state.counter--
},
// 使用常量作为函数名(ES2015 计算属性名)
[INCREMENT_N](state, payload) {
state.counter += payload.num
}
}
})
export default store
(3)然后在组件中使用这个常量:
<template>
<div>
<h4>当前计数:{{ $store.state.counter }}</h4>
<button @click="$store.commit('increment')">+1</button>
<button @click="$store.commit('decrement')">-1</button>
<button @click="addTen">+10</button>
</div>
</template>
<script>
import { INCREMENT_N } from './store/mutation-types'
export default {
methods: {
addTen() {
// 使用常量代替字符串
this.$store.commit(INCREMENT_N, { num: 10, name: 'hangge', age: 18 })
}
}
}
</script>
(4)保存代码后,在浏览器中运行,计数器的 "+10" 功能可以正常使用。

五、mapMutations 辅助函数
1,在 methods 中使用
(1)在 Vuex 中,不仅提供 mapState 函数来简化状态获取,还提供 mapMutations 辅助函数用于简化触发 mutation 函数的操作。
(2)mapMutations 函数的语法与 mapState 函数类似,支持数组和对象两种语法形式:
<template>
<div>
<h4>当前计数:{{ $store.state.counter }}</h4>
<!-- 直接使用映射好的函数 -->
<button @click="increment">+1</button>
<button @click="decrement">-1</button>
<button @click="incrementN({ num: 20 })">+20</button>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
import { INCREMENT_N } from './store/mutation-types'
export default {
methods: {
// 数组语法:将 this.increment() 映射为 this.$store.commit('increment')
...mapMutations(['increment']),
// 对象语法:将 this.incrementN() 映射为 this.$store.commit(INCREMENT_N)
...mapMutations({
incrementN: INCREMENT_N
})
}
}
</script>
(3)可以看到,mapMutations 函数返回的对象中包含了对应映射好的函数,当我们为 methods 解构返回值时,在模板上就可以直接使用这些映射好的函数。

2,在 setup 中使用
(1)在 setup 函数中使用 mapMutations 时,可以直接解构返回,不需要像 mapState 那样进行额外封装:
注意:mapMutations 和 mapActions 可以在 setup 中直接展开使用,因为它们返回的是函数而不是计算属性。而 mapState 和 mapGetters 返回的是需要响应式的计算属性,需要额外封装。
<template>
<div>
<h4>当前计数:{{ $store.state.counter }}</h4>
<button @click="increment">+1</button>
<button @click="decrement">-1</button>
<button @click="incrementN({ num: 20 })">+20</button>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
export default {
setup() {
// 在 setup 中直接解构使用
const storeMutations = mapMutations(['decrement'])
return {
...storeMutations
}
},
methods: {
...mapMutations(['increment']),
...mapMutations({
incrementN: 'incrementN'
})
}
}
</script>
(2)可以看到,mapMutations 在 methods 和 setup 中都可以使用,这两种方式只是语法不同,效果是一样的。

全部评论(0)