Vuex 是什么镰官?
Vuex 是一個專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式拓哺。它采用集中式存儲管理應(yīng)用的所有組件的狀態(tài)哨苛,并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測的方式發(fā)生變化。Vuex 也集成到 Vue 的官方調(diào)試工具 devtools extension茅坛,提供了諸如零配置的 time-travel 調(diào)試、狀態(tài)快照導(dǎo)入導(dǎo)出等高級調(diào)試功能眠屎。
使用
在 Vue 的單頁面應(yīng)用中使用阿宅,需要使用Vue.use(Vuex)調(diào)用插件,將其注入到Vue根實(shí)例中绒窑。
import Vuex from 'vuex'
Vue.use(Vuex)
// 各組件相互共享數(shù)據(jù)的插件
export default new Vuex.Store({
// 數(shù)據(jù)存放地方
state: {
},
// 方法存放地方
mutations: {
},
// 反復(fù)存放地方(可以執(zhí)行ajax)
actions: {
},
// 模塊
modules: {
}
})
核心
- State葬燎,Getter郊丛,Mutation,Action,Module困檩,
Vuex 主要有四部分:
- state:包含了store中存儲的各個狀態(tài)逢唤。
- getter: 類似于 Vue 中的計(jì)算屬性蜻展,根據(jù)其他 getter 或 state 計(jì)算返回值。
- mutation: 一組方法蠕搜,是改變store中狀態(tài)的執(zhí)行者,只能是同步操作岳颇。
- action: 一組方法,其中可以包含異步操作原献。
State
Vuex 使用 state 來存儲應(yīng)用中需要共享的狀態(tài)叮盘。為了能讓 Vue 組件在 state更改后也隨著更改培漏,需要基于state 創(chuàng)建計(jì)算屬性。
// 創(chuàng)建一個 Counter 組件
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return this.$store.state.count // count 為某個狀態(tài)
}
}
}
Getter
類似于 Vue 中的 計(jì)算屬性(可以認(rèn)為是 store 的計(jì)算屬性),getter 的返回值會根據(jù)它的依賴被緩存起來习瑰,且只有當(dāng)它的依賴值發(fā)生了改變才會被重新計(jì)算。
Getter 方法接受 state 作為其第一個參數(shù):
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})
通過屬性訪問
Getter 會暴露為 store.getters 對象,可以以屬性的形式訪問這些值:
store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]
Getter 方法也接受 state和其他getters作為前兩個參數(shù)润讥。
getters: {
// ...
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
store.getters.doneTodosCount // -> 1
我們可以很容易地在任何組件中使用它:
computed: {
doneTodosCount () {
return this.$store.getters.doneTodosCount
}
}
注意: getter 在通過屬性訪問時是作為 Vue 的響應(yīng)式系統(tǒng)的一部分緩存其中的常潮。
通過方法訪問
也可以通過讓 getter 返回一個函數(shù)献联,來實(shí)現(xiàn)給 getter 傳參胁镐。在對 store 里的數(shù)組進(jìn)行查詢時非常有用。
getters: {
// ...
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
注意: getter 在通過方法訪問時阿弃,每次都會去進(jìn)行調(diào)用,而不會緩存結(jié)果伴箩。
Mutation
更改 Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation。也就是說竟闪,前面兩個都是狀態(tài)值本身暗挑,mutations才是改變狀態(tài)的執(zhí)行者也殖。
注意:mutations只能是同步地更改狀態(tài)土思。
Vuex 中的 mutation 非常類似于事件:每個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調(diào)函數(shù) (handler)务热。這個回調(diào)函數(shù)就是我們實(shí)際進(jìn)行狀態(tài)更改的地方,并且它會接受 state 作為第一個參數(shù):
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// 變更狀態(tài)
state.count++
}
}
})
調(diào)用 store.commit 方法:
store.commit('increment')
提交載荷(Payload)
// ...
mutations: {
increment (state, n) {
state.count += n
}
}
this.$store.commit('increment', 10)
其中己儒,第一個參數(shù)是state崎岂,后面的參數(shù)是向 store.commit 傳入的額外的參數(shù),即 mutation 的 載荷(payload)闪湾。
store.commit方法的第一個參數(shù)是要發(fā)起的mutation類型名稱冲甘,后面的參數(shù)均當(dāng)做額外數(shù)據(jù)傳入mutation定義的方法中。
規(guī)范的發(fā)起mutation的方式如下:
// 以載荷形式
store.commit('increment'途样,{
amount: 10 //這是額外的參數(shù)
})
// 或者使用對象風(fēng)格的提交方式
store.commit({
type: 'increment',
amount: 10 //這是額外的參數(shù)
})
額外的參數(shù)會封裝進(jìn)一個對象江醇,作為第二個參數(shù)傳入mutation
定義的方法中。
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
Action
想要異步地更改狀態(tài)何暇,就需要使用 action陶夜。action并不直接改變state,而是發(fā)起mutation裆站。
注冊一個簡單的 action:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
Action 函數(shù)接受一個與 store 實(shí)例具有相同方法和屬性的 context 對象律适,因此你可以調(diào)用 context.commit 提交一個 mutation,或者通過 context.state 和 context.getters 來獲取 state 和 getters遏插。
actions: {
increment ({ commit }) {
commit('increment')
}
}
在action內(nèi)部執(zhí)行異步操作:
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
發(fā)起action的方法形式和發(fā)起mutation一樣捂贿,只是換了個名字dispatch。
// 以對象形式分發(fā)Action
store.dispatch({
type: 'incrementAsync',
amount: 10
})
Actions 支持同樣的載荷方式和對象方式進(jìn)行分發(fā)
Action處理異步的正確使用方式
想要使用action處理異步工作很簡單胳嘲,只需要將異步操作放到action中執(zhí)行要想在異步操作完成后繼續(xù)進(jìn)行相應(yīng)的流程操作厂僧,有兩種方式:
-
store.dispatch返回相應(yīng)action的執(zhí)行結(jié)果,而action的處理函數(shù)返回的就是Promise了牛,所以store.dispatch仍然返回一個Promise颜屠。
actions: { actionA ({ commit }) { return new Promise((resolve, reject) => { setTimeout(() => { commit('someMutation') resolve() }, 1000) }) } }
現(xiàn)在可以寫成:
store.dispatch('actionA').then(() => { // ... })
在另外一個 action 中也可以:
actions: { // ... actionB ({ dispatch, commit }) { return dispatch('actionA').then(() => { commit('someOtherMutation') }) } }
-
利用async/await 進(jìn)行組合action。代碼更加簡潔鹰祸。
// 假設(shè) getData() 和 getOtherData() 返回的是 Promise actions: { async actionA ({ commit }) { commit('gotData', await getData()) }, async actionB ({ dispatch, commit }) { await dispatch('actionA') // 等待 actionA 完成 commit('gotOtherData', await getOtherData()) } }
一個 store.dispatch在不同模塊中可以觸發(fā)多個 action 函數(shù)甫窟。在這種情況下,只有當(dāng)所有觸發(fā)函數(shù)完成后蛙婴,返回的 Promise 才會執(zhí)行粗井。
Action與Mutation的區(qū)別
Action 類似于 mutation,不同在于:
- Action 提交的是 mutation街图,而不是直接變更狀態(tài)浇衬。
- Action 可以包含任意異步操作,而Mutation只能且必須是同步操作餐济。
Module
由于使用單一狀態(tài)樹耘擂,應(yīng)用的所有狀態(tài)會集中到一個比較大的對象。當(dāng)應(yīng)用變得非常復(fù)雜時絮姆,store 對象就有可能變得相當(dāng)臃腫醉冤。
這時我們可以將 store 分割為模塊(module)秩霍,每個模塊擁有自己的
state
、getters
蚁阳、mutations
前域、actions
、甚至是嵌套子模塊——從上至下進(jìn)行同樣方式的分割韵吨。
代碼示例:
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的狀態(tài)
store.state.b // -> moduleB 的狀態(tài)