1、定義
Vuex 是一個(gè)專(zhuān)為 Vue.js 應(yīng)用程序開(kāi)發(fā)的狀態(tài)管理模式镶殷。它采用集中式存儲(chǔ)管理應(yīng)用的所有組件的狀態(tài)利花,并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測(cè)的方式發(fā)生變化。
vuex解決的問(wèn)題:
- 多個(gè)視圖依賴(lài)于同一狀態(tài)泊藕。
- 來(lái)自不同視圖的行為需要變更同一狀態(tài)辅辩。
2、state
Vuex 使用單一狀態(tài)樹(shù)吱七,每個(gè)應(yīng)用將僅僅包含一個(gè) store 實(shí)例汽久。單一狀態(tài)樹(shù)讓我們能夠直接地定位任一特定的狀態(tài)片段,在調(diào)試的過(guò)程中也能輕易地取得整個(gè)當(dāng)前應(yīng)用狀態(tài)的快照踊餐。
Vuex 通過(guò) store 選項(xiàng)景醇,提供了一種機(jī)制將狀態(tài)從根組件“注入”到每一個(gè)子組件中(需調(diào)用 Vue.use(Vuex)):
const app = new Vue({
el: '#app',
// 把 store 對(duì)象提供給 “store” 選項(xiàng),這可以把 store 的實(shí)例注入所有的子組件
store,
components: { Counter },
template: `
<div class="app">
<counter></counter>
</div>
`
})
通過(guò)在根實(shí)例中注冊(cè) store 選項(xiàng)吝岭,該 store 實(shí)例會(huì)注入到根組件下的所有子組件中三痰,且子組件能通過(guò) this.$store 訪問(wèn)到。
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return this.$store.state.count
}
}
}
- mapState 輔助函數(shù)
當(dāng)一個(gè)組件需要獲取多個(gè)狀態(tài)時(shí)候窜管,將這些狀態(tài)都聲明為計(jì)算屬性會(huì)有些重復(fù)和冗余散劫。為了解決這個(gè)問(wèn)題,我們可以使用 mapState 輔助函數(shù)幫助我們生成計(jì)算屬性幕帆,讓你少按幾次鍵:
/ 在單獨(dú)構(gòu)建的版本中輔助函數(shù)為 Vuex.mapState
import { mapState } from 'vuex'
export default {
// ...
computed: mapState({
// 箭頭函數(shù)可使代碼更簡(jiǎn)練
count: state => state.count,
// 傳字符串參數(shù) 'count' 等同于 `state => state.count`
countAlias: 'count',
// 為了能夠使用 `this` 獲取局部狀態(tài)获搏,必須使用常規(guī)函數(shù)
countPlusLocalState (state) {
return state.count + this.localCount
}
})
}
當(dāng)映射的計(jì)算屬性的名稱(chēng)與 state 的子節(jié)點(diǎn)名稱(chēng)相同時(shí),我們也可以給 mapState 傳一個(gè)字符串?dāng)?shù)組失乾。
computed: mapState([
// 映射 this.count 為 store.state.count
'count'
])
mapState
函數(shù)返回的是一個(gè)對(duì)象常熙。我們?nèi)绾螌⑺c局部計(jì)算屬性混合使用呢?通常碱茁,我們需要使用一個(gè)工具函數(shù)將多個(gè)對(duì)象合并為一個(gè)裸卫,以使我們可以將最終對(duì)象傳給 computed
屬性。但是自從有了對(duì)象展開(kāi)運(yùn)算符(現(xiàn)處于 ECMASCript 提案 stage-3 階段)纽竣,我們可以極大地簡(jiǎn)化寫(xiě)法:
computed: {
localComputed () { /* ... */ },
// 使用對(duì)象展開(kāi)運(yùn)算符將此對(duì)象混入到外部對(duì)象中
...mapState({
// ...
})
}
3墓贿、Getter
Vuex 允許我們?cè)?store 中定義“getter”(可以認(rèn)為是 store 的計(jì)算屬性)茧泪。就像計(jì)算屬性一樣,getter 的返回值會(huì)根據(jù)它的依賴(lài)被緩存起來(lái)聋袋,且只有當(dāng)它的依賴(lài)值發(fā)生了改變才會(huì)被重新計(jì)算队伟。
Getter 接受 state 作為其第一個(gè)參數(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 會(huì)暴露為 store.getters 對(duì)象:
store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]
Getter 也可以接受其他 getter 作為第二個(gè)參數(shù):
getters: {
// ...
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
store.getters.doneTodosCount // -> 1
我們可以很容易地在任何組件中使用它:
computed: {
doneTodosCount () {
return this.$store.getters.doneTodosCount
}
}
你也可以通過(guò)讓 getter 返回一個(gè)函數(shù),來(lái)實(shí)現(xiàn)給 getter 傳參舱馅。在你對(duì) store 里的數(shù)組進(jìn)行查詢(xún)時(shí)非常有用缰泡。
getters: {
// ...
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
4、Mutation
更改 Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation代嗤。
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// 變更狀態(tài)
state.count++
}
}
})
你不能直接調(diào)用一個(gè) mutation handler棘钞。這個(gè)選項(xiàng)更像是事件注冊(cè):“當(dāng)觸發(fā)一個(gè)類(lèi)型為 increment 的 mutation 時(shí),調(diào)用此函數(shù)干毅∫瞬拢”要喚醒一個(gè) mutation handler,你需要以相應(yīng)的 type 調(diào)用 store.commit 方法:
store.commit('increment')
你可以向 store.commit 傳入額外的參數(shù)硝逢,即 mutation 的 載荷(payload):
// ...
mutations: {
increment (state, n) {
state.count += n
}
}
store.commit('increment', 10)
!Mutation 必須是同步函數(shù)
4姨拥、Mutation 必須是同步函數(shù)
Action 類(lèi)似于 mutation,不同在于:
- Action 提交的是 mutation渠鸽,而不是直接變更狀態(tài)叫乌。
- Action 可以包含任意異步操作。
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
實(shí)踐中徽缚,我們會(huì)經(jīng)常用到 ES2015 的 參數(shù)解構(gòu) 來(lái)簡(jiǎn)化代碼(特別是我們需要調(diào)用 commit
很多次的時(shí)候):
actions: {
increment ({ commit }) {
commit('increment')
}
}
Action 通過(guò) store.dispatch 方法觸發(fā):
store.dispatch('increment')
我們可以在 action 內(nèi)部執(zhí)行異步操作:
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
Actions 支持同樣的載荷方式和對(duì)象方式進(jìn)行分發(fā):
// 以載荷形式分發(fā)
store.dispatch('incrementAsync', {
amount: 10
})
// 以對(duì)象形式分發(fā)
store.dispatch({
type: 'incrementAsync',
amount: 10
})
5憨奸、Module
由于使用單一狀態(tài)樹(shù),應(yīng)用的所有狀態(tài)會(huì)集中到一個(gè)比較大的對(duì)象凿试。當(dāng)應(yīng)用變得非常復(fù)雜時(shí)排宰,store 對(duì)象就有可能變得相當(dāng)臃腫。
為了解決以上問(wèn)題那婉,Vuex 允許我們將 store 分割成模塊(module)板甘。每個(gè)模塊擁有自己的 state、mutation详炬、action盐类、getter、甚至是嵌套子模塊——從上至下進(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)