Mutation规则 #
核心规则 #
Vuex 的 Mutation 有几条必须遵守的规则:
text
Mutation 规则
├── 必须是同步函数
├── 必须通过 commit 调用
├── 第一个参数是 state
└── 不应直接修改 state(在组件中)
规则一:必须是同步函数 #
为什么? #
Mutation 必须是同步函数,这是 Vuex 设计的核心原则。原因如下:
javascript
// 问题:异步 mutation
mutations: {
INCREMENT(state) {
setTimeout(() => {
state.count++
}, 1000)
}
}
// 当 commit 完成时,状态实际上还没有改变
this.$store.commit('INCREMENT')
console.log(this.$store.state.count) // 还是旧值
正确做法 #
javascript
// Mutation 保持同步
mutations: {
INCREMENT(state) {
state.count++
}
}
// 异步操作放在 Action 中
actions: {
async incrementAsync({ commit }) {
await delay(1000)
commit('INCREMENT')
}
}
调试问题 #
异步 mutation 会导致调试困难:
text
DevTools 记录:
┌─────────────────────────────────────────┐
│ Mutation: INCREMENT │
│ 时间: 10:00:00 │
│ 状态变化: count 0 → 1 │
└─────────────────────────────────────────┘
如果 mutation 是异步的:
- DevTools 记录的是 commit 时间
- 实际状态变化发生在之后
- 时间旅行调试会出错
规则二:必须通过 commit 调用 #
错误示例 #
javascript
// 错误:直接调用 mutation
this.$store.mutations.INCREMENT(this.$store.state)
// 错误:直接修改 state
this.$store.state.count = 10
正确做法 #
javascript
// 正确:通过 commit
this.$store.commit('INCREMENT')
// 正确:通过 Action
this.$store.dispatch('increment')
严格模式 #
开启严格模式可以防止非法修改:
javascript
const store = createStore({
strict: true, // 开发环境开启
state: { /* ... */ },
mutations: { /* ... */ }
})
// 直接修改会抛出错误
this.$store.state.count = 10 // Error!
规则三:响应式更新 #
添加新属性 #
javascript
// 问题:直接添加属性不会触发响应式
mutations: {
ADD_PROPERTY(state) {
state.user.newProp = 'value' // 不会触发更新
}
}
// 解决方案一:使用 Vue.set
import { set } from 'vue'
mutations: {
ADD_PROPERTY(state) {
set(state.user, 'newProp', 'value')
}
}
// 解决方案二:对象展开
mutations: {
ADD_PROPERTY(state) {
state.user = { ...state.user, newProp: 'value' }
}
}
数组操作 #
javascript
// 问题:通过索引修改
mutations: {
UPDATE_ITEM(state, { index, value }) {
state.items[index] = value // 不会触发更新
}
}
// 解决方案一:使用 splice
mutations: {
UPDATE_ITEM(state, { index, value }) {
state.items.splice(index, 1, value)
}
}
// 解决方案二:创建新数组
mutations: {
UPDATE_ITEM(state, { index, value }) {
state.items = state.items.map((item, i) =>
i === index ? value : item
)
}
}
删除属性 #
javascript
// 问题:直接删除
mutations: {
REMOVE_PROPERTY(state) {
delete state.user.oldProp // 不会触发更新
}
}
// 解决方案:使用 Vue.delete 或对象解构
import { del } from 'vue'
mutations: {
REMOVE_PROPERTY(state) {
del(state.user, 'oldProp')
// 或
const { oldProp, ...rest } = state.user
state.user = rest
}
}
规则四:命名规范 #
使用常量 #
javascript
// mutation-types.js
export const SET_USER = 'SET_USER'
export const SET_LOADING = 'SET_LOADING'
export const ADD_TO_CART = 'ADD_TO_CART'
// store.js
import * as types from './mutation-types'
mutations: {
[types.SET_USER](state, user) {
state.user = user
},
[types.SET_LOADING](state, loading) {
state.loading = loading
},
[types.ADD_TO_CART](state, item) {
state.cart.push(item)
}
}
命名约定 #
javascript
// 推荐:使用动词+名词
mutations: {
SET_USER,
SET_LOADING,
ADD_ITEM,
REMOVE_ITEM,
UPDATE_TODO,
CLEAR_CART
}
// 不推荐:模糊命名
mutations: {
user, // 不清楚是设置还是获取
loading,
item
}
规则五:单一职责 #
每个 Mutation 只做一件事 #
javascript
// 推荐:单一职责
mutations: {
SET_USER(state, user) {
state.user = user
},
SET_LOADING(state, loading) {
state.loading = loading
},
SET_ERROR(state, error) {
state.error = error
}
}
// 不推荐:一个 mutation 做多件事
mutations: {
FETCH_USER_SUCCESS(state, { user, loading, error }) {
state.user = user
state.loading = loading
state.error = error
}
}
在 Action 中组合 #
javascript
// Action 可以组合多个 mutation
actions: {
async fetchUser({ commit }, userId) {
commit('SET_LOADING', true)
commit('SET_ERROR', null)
try {
const user = await api.fetchUser(userId)
commit('SET_USER', user)
} catch (error) {
commit('SET_ERROR', error.message)
} finally {
commit('SET_LOADING', false)
}
}
}
开发环境配置 #
开启严格模式 #
javascript
const store = createStore({
// 只在开发环境开启
strict: process.env.NODE_ENV !== 'production',
state: { /* ... */ },
mutations: { /* ... */ }
})
性能考虑 #
严格模式会深度观察状态树,可能影响性能:
javascript
// 生产环境关闭严格模式
if (process.env.NODE_ENV === 'production') {
store.strict = false
}
常见错误 #
1. 在 Mutation 中进行异步操作 #
javascript
// 错误
mutations: {
FETCH_USER(state) {
fetch('/api/user').then(user => {
state.user = user // 异步修改
})
}
}
// 正确
actions: {
async fetchUser({ commit }) {
const user = await fetch('/api/user')
commit('SET_USER', user)
}
}
2. 在组件中直接修改 state #
javascript
// 错误
this.$store.state.user.name = 'John'
// 正确
this.$store.commit('SET_USER_NAME', 'John')
3. Mutation 返回值 #
javascript
// 错误:依赖 mutation 返回值
const result = this.$store.commit('INCREMENT')
// Mutation 不应该有返回值
mutations: {
INCREMENT(state) {
state.count++
return state.count // 不要这样做
}
}
总结 #
Mutation 规则总结:
| 规则 | 说明 | 原因 |
|---|---|---|
| 同步函数 | 必须同步执行 | 便于调试和追踪 |
| commit 调用 | 必须通过 commit | 统一入口,便于管理 |
| 响应式更新 | 正确添加/删除属性 | 确保视图更新 |
| 命名规范 | 使用常量和清晰命名 | 提高可维护性 |
| 单一职责 | 每个 mutation 只做一件事 | 便于测试和调试 |
继续学习 Action基础,了解如何处理异步操作。
最后更新:2026-03-28