Vuex好難-Mutation

KY
5 min readApr 13, 2022

文章出處網站

更改 Vuex 的store 中的狀態的唯一方法是提交 mutation。Vuex 中的 mutation 非常類似於事件:每個 mutation 都有一個字符串的 事件類型(type)和一個回調函數(handler)。這個回調函數就是我們實際進行狀態更改的地方,並且它會接受 state 作為第一個參數:

const store = createStore({
state: {
count: 1
},
mutations: {
increment (state) {
// 变更状态
state.count++
}
}
})

你不能直接調用一個 mutation 處理函數。這個選項更像是事件註冊:”當觸發一個類型為” increment 的 mutation 時,調用此函數。” 要喚醒一個 mutation 處理函數,你需要以相應的 type 調用 store.commit 方法:”

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
})

對象風格的提交方式

— — — — — —

提交 mutation 的另外一種方式是直接使用包含 type 屬性的對象:

store.commit({
type: 'increment',
amount: 10
})

當使用對象風格的提交方式,整個對像都作為載荷傳給 mutation 函數,因此處理函數保持不變:

mutations: {
increment (state, payload) {
state.count += payload.amount
}
}

使用常量替代 Mutation 事件類型

— — — — — —

使用常量替代 mutation 事件類型在各種 Flux 實現中是很常見的模式。這樣可以使 linter 之類的工具發揮作用,同時把這些常量放在單獨的文件中可以讓你的代碼合作對整個 app 包含的 mutation 一目瞭然:

// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
// store.js
import { createStore } from 'vuex'
import { SOME_MUTATION } from './mutation-types'

const store = createStore({
state: { ... },
mutations: {
// 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
[SOME_MUTATION] (state) {
// 修改 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 ,或著使用 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')`
})
}
}

下一步:Action

— — — — — —

在 mutation 中混合異步調用會導致你的程序很難調適,例如,當你調用了兩個包含異步回調的 mutation 來改變狀態,你怎麼知道什麼時候回調和哪個先回調呢?

這就是為什麼我們要區分這兩個概念。

在 Vuex 中,mutation 都是同步事務:

store.commit('increment')
// 任何由 "increment" 导致的状态变更都应该在此刻完成。

除了處理異步操作,讓我們來看一看 Action 。

--

--