寫在前面
上一講「Vuex 旗下的 State 和 Getter」压彭,告訴了我們怎么去使用倉庫 store 中的狀態(tài)數(shù)據(jù)。當然盯桦,光會用肯定還不夠绰寞,大部分的應用場景還得對這些狀態(tài)進行操控撮竿,那么具體如何操控呢,這就是這一講要說的重點骆姐。
只有 mutation 能動 State
更改 Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation镜粤。Vuex 中的 mutation 非常類似于事件:每個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調(diào)函數(shù) (handler)捏题。這個回調(diào)函數(shù)就是我們實際進行狀態(tài)更改的地方,并且它會接受 state 作為第一個參數(shù):
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
// 事件類型 type 為 increment
increment (state) {
// 變更狀態(tài)
state.count++
}
}
})
注意肉渴,我們不能直接 store.mutations.increment()
來調(diào)用公荧,Vuex 規(guī)定必須使用 store.commit
來觸發(fā)對應 type 的方法:
store.commit('increment')
傳參
我們還可以向 store.commit 傳入額外的參數(shù):
mutations: {
increment (state, n) {
state.count += n
}
}
// 調(diào)用
store.commit('increment', 10)
mutation 中的這個額外的參數(shù),官方給它還取了一個高大上的名字:載荷(payload)同规。說實話循狰,第一次在文檔中看到這個標題「提交載荷」,真的就不想往下看了捻浦。
我們往往不是敗給了這些生澀的概念晤揣,而是敗給了自己內(nèi)心的恐懼。
大多數(shù)情況下朱灿,載荷是一個對象昧识,能夠讓我們更加易讀:
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
關(guān)于提交的方式,有兩種:
// 1盗扒、把載荷和type分開提交
store.commit('increment', {
amount: 10
})
// 2跪楞、整個對象都作為載荷傳給 mutation 函數(shù)
store.commit({
type: 'increment',
amount: 10
})
當然,使用哪種方式?jīng)]有絕對的界限侣灶,純看自己的喜好甸祭,就我個人而言,還是比較傾向于使用第二種姿勢褥影,放在一起更實在池户。
修改規(guī)則
簡單修改基礎類型的狀態(tài)數(shù)據(jù)倒是簡單,沒什么限制凡怎,但是如果修改的是對象校焦,那就要注意了。比如這個例子:
const store = new Vuex.Store({
state: {
student: {
name: '小明',
sex: '女'
}
}
})
這個時候统倒,我們?nèi)绻胍o student
添加一個年齡 age: 18
屬性寨典,怎么辦呢?
是的房匆,直接在 sex
下面把這個字段加上去不就行了耸成,能這樣當然最好了。但是如果我們要動態(tài)的修改呢浴鸿?那就得遵循 Vue 的規(guī)則了井氢。如下:
mutations: {
addAge (state) {
Vue.set(state.student, 'age', 18)
// 或者:
// state.student = { ...state.student, age: 18 }
}
}
以上就是給對象添加屬性的兩種方式,當然赚楚,對于已添加的對象毙沾,如果想修改具體值的話,直接更改就是宠页,比如 state.student.age=20
即可左胞。
至于為什么要這樣寇仓,之前我們了解過,因為 store 中的狀態(tài)是響應式的烤宙,當我們更改狀態(tài)數(shù)據(jù)的時候遍烦,監(jiān)視狀態(tài)的 Vue 組件也會自動更新,所以 Vuex 中的 mutation 也需要與使用 Vue 一樣遵守這些規(guī)則躺枕。
使用常量
就是使用常量來替代 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) {
// mutate state
}
}
})
可能有人會有疑問啊,這樣做到底有啥用拐云,還得多創(chuàng)建個類型文件罢猪,用的時候還要導入進來,不嫌麻煩嗎叉瘩!
我們看看膳帕,mutation 是怎么調(diào)用的:store.commit('increment')
,可以發(fā)現(xiàn)薇缅,這里 commit 提交的方法 increment
危彩,是以字符串的形式代入的。如果項目小泳桦,一個人開發(fā)的話倒還好汤徽,但是項目大了,編寫代碼的人多了灸撰,那就麻煩了谒府,因為需要 commit 的方法一多,就會顯得特別混亂浮毯,而且以字符串形式代入的話狱掂,一旦出了錯,很難排查亲轨。
所以,對于多人合作的大項目鸟顺,最好還是用常量的形式來處理 mutation惦蚊,對于小項目倒是無所謂,想偷懶的隨意就好讯嫂。
必須是同步函數(shù)
一定要記住蹦锋,Mutation 必須是同步函數(shù)。為什么呢欧芽?
前面說了莉掂,我們之所以要通過提交 mutation 的方式來改變狀態(tài)數(shù)據(jù),是因為我們想要更明確地追蹤到狀態(tài)的變化千扔。如果像下面這樣異步的話:
mutations: {
someMutation (state) {
api.callAsyncMethod(() => {
state.count++
})
}
}
我們就不知道什么時候狀態(tài)會發(fā)生改變憎妙,所以也就無法追蹤了库正,這與 Mutation 的設計初心相悖,所以強制規(guī)定它必須是同步函數(shù)厘唾。
store.commit('increment')
// 任何由 "increment" 導致的狀態(tài)變更都應該在此刻完成褥符。