?
This document uses PHP Chinese website manual Release
在 Vuex store 中,實際改變 狀態(tài)(state) 的唯一方式是通過 提交(commit) 一個 mutation。 Vuex 的 mutation 和事件系統(tǒng)非常相似:每個 mutation 都有一個字符串 類型(type) 和 一個 回調(diào)函數(shù)(handler)?;卣{(diào)函數(shù)是我們執(zhí)行實際修改狀態(tài)的地方,它將接收 狀態(tài)(state) 作為第一個參數(shù)。
const store = new Vuex.Store({ state: { count: 1 }, mutations: { increment (state) { // 改變 state state.count++ } } })
你不能直接調(diào)用 mutation 的回調(diào)函數(shù)。選項 mutations 在這里更像是注冊事件:“當觸發(fā)類型為 increment
的 mutation 時,執(zhí)行其回調(diào)函數(shù)?!彼阅阈枰{(diào)用該類型的 store.commit 才能執(zhí)行 mutation 的回調(diào)函數(shù)。
store.commit('increment')
向 store.commit
傳遞一個額外的參數(shù),這個參數(shù)被稱為 payload :
// ... mutations: { increment (state, n) { state.count += n } }
多數(shù)情況下,payload 應(yīng)該是一個對象,以便它可以包含多個字段,這樣 mutation 記錄中有了 payload 字段名,可描述性會變得更好。
// ... mutations: { increment (state, payload) { state.count += payload.amount } }
store.commit('increment', { amount: 10 })
提交 mutation 的另一種替代方式,是直接使用具有 type
屬性的對象:
store.commit({ type: 'increment', amount: 10 })
當使用對象風(fēng)格的 commit,整個對象都會被作為 payload 參數(shù)傳入到對應(yīng)類型的 mutation 的回調(diào)函數(shù)中,不過回調(diào)函數(shù)還保持不變:
mutations: { increment (state, payload) { state.count += payload.amount } }
注意:一旦我們實現(xiàn)了 devtools 中過濾 mutation,此特性可能會被棄用。
默認情況下,每個提交過的 mutation 都會被發(fā)送到插件(如 devtools)。然而在某些情況下,你可能不希望插件去記錄每個狀態(tài)更改。像是在短時間多次提交到 store 或輪詢,并不總是需要跟蹤。在這種情況下你可以在 store.commit
中傳入第三個參數(shù),來指定插件中的 mutation 是否“靜默”。
store.commit('increment', { amount: 1 }, { silent: true }) // 使用對象風(fēng)格的 dispatch store.commit({ type: 'increment', amount: 1 }, { silent: true })
由于 Vue 中 Vuex store 的狀態(tài)是響應(yīng)式的,當我們改變狀態(tài),Vue 組件觀察到狀態(tài)改變將自動更新。這也意味著 Vuex mutation 同樣遵循純 Vue 響應(yīng)式規(guī)則。
推薦預(yù)先初始化 store 中你所需的初始狀態(tài)。
向?qū)ο筇砑有碌膶傩詴r,你應(yīng)該這樣做:
使用 Vue.set(obj, 'newProp', 123)
用新的對象替換該對象。例如,使用 stage-2 對象擴展語法 我們可以這樣寫:
state.obj = { ...state.obj, newProp: 123
在各種 Flux 實現(xiàn)中,使用常量作為 mutation 類型是一種常見的模式。這允許代碼利用工具如 linters,將所有常量放在一個單獨文件中,盡可能使協(xié)作者對整個應(yīng)用的 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 屬性名表達式”功能,來使用常量作為函數(shù)名稱 [SOME_MUTATION] (state) { // 改變狀態(tài) } } })
是否使用常量在很大程度上是一個偏好 - 在多人合作開發(fā)的大型項目中它很有用,但如果你不喜歡使用,它也是完全可選的。
一個重要的原則就是牢記 mutation 必須是同步函數(shù)。為什么?考慮下面的例子:
mutations: { someMutation (state) { api.callAsyncMethod(() => { state.count++ }) } }
現(xiàn)在想象我們正在調(diào)試應(yīng)用程序,并查看 devtool 的 mutation 記錄。每個 mutation 記錄,devtool 將需要捕獲每個狀態(tài)“之前”和“之后”的快照。然而,上面的示例中 mutation 內(nèi)部的異步回調(diào)使得這是不可能的:當 mutation 被提交后,回調(diào)函數(shù)還未被調(diào)用,也沒有辦法讓 devtool 知道回調(diào)函數(shù)在何時被調(diào)用 - 即在回調(diào)函數(shù)中執(zhí)行任意狀態(tài)變更,實際上都無法跟蹤。
可以在組件中使用 this.$store.commit('xxx')
提交 mutation,或者使用 mapMutations
工具遍歷組件方法到 store.commit
的回調(diào)上(需要把 store
注入根組件)
import { mapMutations } from 'vuex' export default { // ... methods: { ...mapMutations([ 'increment' // 映射 this.increment() 到 this.$store.commit('increment') ]), ...mapMutations({ add: 'increment' // 映射 this.add() 到 this.$store.commit('increment') }) } }
在 mutation 中混合異步調(diào)用會導(dǎo)致你的程序很難調(diào)試。例如當你調(diào)用兩個都含有異步回調(diào)的方法去改變狀態(tài),你如何知道他們何時被調(diào)用和哪個回調(diào)被首先調(diào)用?這正是我們分離 Mutation 和 Action 這兩個概念的原因。在 Vuex,Mutation 必須是同步事務(wù):
store.commit('increment') // 類型為 "increment" 的 mutation 提交后,可能引起的任意狀態(tài)變化,都應(yīng)該在此時同步完成
為了處理異步操作,接下來我們介紹 Actions。