Mutation基础 #

什么是 Mutation? #

Mutation 是 Vuex 中更改状态的唯一方法。每个 mutation 都有一个字符串的事件类型 (type) 和一个回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数。

定义 Mutation #

基本语法 #

javascript
const store = createStore({
  state: {
    count: 0
  },
  
  mutations: {
    // mutation 函数
    INCREMENT(state) {
      state.count++
    },
    
    DECREMENT(state) {
      state.count--
    }
  }
})

使用常量命名 #

推荐使用常量作为 mutation 类型名:

javascript
// mutation-types.js
export const INCREMENT = 'INCREMENT'
export const DECREMENT = 'DECREMENT'
export const SET_COUNT = 'SET_COUNT'
export const SET_USER = 'SET_USER'

// store.js
import { INCREMENT, DECREMENT, SET_COUNT, SET_USER } from './mutation-types'

const store = createStore({
  state: {
    count: 0,
    user: null
  },
  
  mutations: {
    [INCREMENT](state) {
      state.count++
    },
    
    [DECREMENT](state) {
      state.count--
    },
    
    [SET_COUNT](state, value) {
      state.count = value
    },
    
    [SET_USER](state, user) {
      state.user = user
    }
  }
})

提交 Mutation #

普通提交 #

javascript
// 在组件中
this.$store.commit('INCREMENT')

// 带 payload
this.$store.commit('SET_COUNT', 10)

对象风格提交 #

javascript
this.$store.commit({
  type: 'SET_COUNT',
  value: 10
})

在 Action 中提交 #

javascript
actions: {
  increment({ commit }) {
    commit('INCREMENT')
  },
  
  setCount({ commit }, value) {
    commit('SET_COUNT', value)
  }
}

Payload 载荷 #

单一参数 #

javascript
mutations: {
  SET_COUNT(state, value) {
    state.count = value
  }
}

// 提交
this.$store.commit('SET_COUNT', 10)

对象参数 #

javascript
mutations: {
  UPDATE_USER(state, payload) {
    state.user = {
      ...state.user,
      ...payload
    }
  }
}

// 提交
this.$store.commit('UPDATE_USER', {
  name: 'John',
  age: 25
})

解构参数 #

javascript
mutations: {
  UPDATE_USER(state, { name, age }) {
    state.user.name = name
    state.user.age = age
  }
}

// 提交
this.$store.commit('UPDATE_USER', {
  name: 'John',
  age: 25
})

Mutation 规则 #

1. 必须是同步函数 #

javascript
// 错误:异步 mutation
mutations: {
  INCREMENT(state) {
    setTimeout(() => {
      state.count++  // 不要这样做
    }, 1000)
  }
}

// 正确:同步 mutation
mutations: {
  INCREMENT(state) {
    state.count++
  }
}

// 异步操作应该放在 action 中
actions: {
  async incrementAsync({ commit }) {
    await delay(1000)
    commit('INCREMENT')
  }
}

2. 不要直接调用 mutation #

javascript
// 错误:直接调用
this.$store.mutations.INCREMENT(state)

// 正确:通过 commit
this.$store.commit('INCREMENT')

3. 使用 Vue.set 处理新属性 #

javascript
mutations: {
  ADD_PROPERTY(state, { key, value }) {
    // 错误:直接添加属性不会触发响应式
    // state.user[key] = value
    
    // 正确:使用 Vue.set 或对象展开
    state.user = { ...state.user, [key]: value }
  }
}

常见 Mutation 模式 #

设置值 #

javascript
mutations: {
  SET_USER(state, user) {
    state.user = user
  },
  
  SET_LOADING(state, loading) {
    state.loading = loading
  },
  
  SET_ERROR(state, error) {
    state.error = error
  }
}

添加项 #

javascript
mutations: {
  ADD_TODO(state, todo) {
    state.todos.push(todo)
  },
  
  ADD_TO_CART(state, item) {
    state.cart.push(item)
  }
}

删除项 #

javascript
mutations: {
  REMOVE_TODO(state, id) {
    const index = state.todos.findIndex(t => t.id === id)
    if (index !== -1) {
      state.todos.splice(index, 1)
    }
  },
  
  CLEAR_CART(state) {
    state.cart = []
  }
}

更新项 #

javascript
mutations: {
  UPDATE_TODO(state, { id, updates }) {
    const index = state.todos.findIndex(t => t.id === id)
    if (index !== -1) {
      state.todos[index] = {
        ...state.todos[index],
        ...updates
      }
    }
  },
  
  TOGGLE_TODO(state, id) {
    const todo = state.todos.find(t => t.id === id)
    if (todo) {
      todo.done = !todo.done
    }
  }
}

切换状态 #

javascript
mutations: {
  TOGGLE_SIDEBAR(state) {
    state.sidebarOpen = !state.sidebarOpen
  },
  
  TOGGLE_THEME(state) {
    state.theme = state.theme === 'light' ? 'dark' : 'light'
  }
}

使用 mapMutations #

数组语法 #

javascript
import { mapMutations } from 'vuex'

export default {
  methods: {
    ...mapMutations([
      'INCREMENT',
      'DECREMENT',
      'SET_COUNT'
    ])
  }
}

// 使用
this.INCREMENT()
this.SET_COUNT(10)

对象语法 #

javascript
import { mapMutations } from 'vuex'

export default {
  methods: {
    ...mapMutations({
      add: 'INCREMENT',
      subtract: 'DECREMENT',
      set: 'SET_COUNT'
    })
  }
}

// 使用
this.add()
this.set(10)

模块中的使用 #

javascript
import { mapMutations } from 'vuex'

export default {
  methods: {
    ...mapMutations('user', [
      'SET_USER',
      'CLEAR_USER'
    ]),
    
    ...mapMutations('cart', {
      addToCart: 'ADD_ITEM',
      clearCart: 'CLEAR'
    })
  }
}

组合式 API 中使用 #

vue
<script>
import { useStore } from 'vuex'

export default {
  setup() {
    const store = useStore()
    
    const increment = () => {
      store.commit('INCREMENT')
    }
    
    const setCount = (value) => {
      store.commit('SET_COUNT', value)
    }
    
    return {
      increment,
      setCount
    }
  }
}
</script>

实战示例 #

用户状态管理 #

javascript
// store/modules/user.js
export default {
  namespaced: true,
  
  state: {
    user: null,
    token: null,
    loading: false,
    error: null
  },
  
  mutations: {
    SET_USER(state, user) {
      state.user = user
    },
    
    SET_TOKEN(state, token) {
      state.token = token
    },
    
    SET_LOADING(state, loading) {
      state.loading = loading
    },
    
    SET_ERROR(state, error) {
      state.error = error
    },
    
    CLEAR_USER(state) {
      state.user = null
      state.token = null
    },
    
    CLEAR_ERROR(state) {
      state.error = null
    }
  }
}

购物车状态管理 #

javascript
// store/modules/cart.js
export default {
  namespaced: true,
  
  state: {
    items: [],
    couponCode: null
  },
  
  mutations: {
    ADD_ITEM(state, { product, quantity = 1 }) {
      const existingItem = state.items.find(i => i.productId === product.id)
      
      if (existingItem) {
        existingItem.quantity += quantity
      } else {
        state.items.push({
          productId: product.id,
          product,
          quantity
        })
      }
    },
    
    REMOVE_ITEM(state, productId) {
      const index = state.items.findIndex(i => i.productId === productId)
      if (index !== -1) {
        state.items.splice(index, 1)
      }
    },
    
    UPDATE_QUANTITY(state, { productId, quantity }) {
      const item = state.items.find(i => i.productId === productId)
      if (item) {
        item.quantity = quantity
      }
    },
    
    SET_COUPON(state, code) {
      state.couponCode = code
    },
    
    CLEAR_CART(state) {
      state.items = []
      state.couponCode = null
    }
  }
}

总结 #

Mutation 核心要点:

要点 说明
同步性 必须是同步函数
提交方式 通过 commit 调用
参数传递 使用 payload 对象
命名规范 使用常量命名

继续学习 提交风格,了解不同的提交方式。

最后更新:2026-03-28