一、概念
Vuex是一個(gè)專為 Vue.js應(yīng)用程序開發(fā)的 狀態(tài)管理模式。
因?yàn)槟K間是不共享作用域的,平時(shí)用來解決共享參數(shù)的方法就是通過組件傳參,一旦項(xiàng)目變得很龐大,管理和維護(hù)這些值將是相當(dāng)棘手的事情。
這是我們就可以使用Vuex集中式存儲(chǔ)管理所有組件的狀態(tài)。
二、整體運(yùn)作流程

三、使用場景
如果您不打算開發(fā)大型單頁應(yīng)用,使用 Vuex 可能是繁瑣冗余的。確實(shí)是如此——如果您的應(yīng)用夠簡單,您最好不要使用 Vuex。一個(gè)簡單的 store 模式就足夠您所需了。但是,如果您需要構(gòu)建一個(gè)中大型單頁應(yīng)用,您很可能會(huì)考慮如何更好地在組件外部管理狀態(tài),Vuex 將會(huì)成為自然而然的選擇。
四、為啥不使用其他的狀態(tài)管理器,如redux
因?yàn)? Vuex是一個(gè)專門為 Vue應(yīng)用所設(shè)計(jì)。這使得它能夠更好地和Vue進(jìn)行整合,同時(shí)提供簡潔的 API 和改善過的開發(fā)體驗(yàn)。
五、五大核心
State、Getters、Mutations、Actions、Modules
mapState、mapGetters、mapMutations、mapActions
可以把 Vuex理解為 “前端的數(shù)據(jù)庫”。
state 就是數(shù)據(jù)庫。
getters 是用來從數(shù)據(jù)庫取數(shù)據(jù)的。既然是取,那么肯定是不能修改的,所以, getters 是一個(gè)“純函數(shù)”,即不會(huì)對元數(shù)據(jù)造成影響的函數(shù)。
mutations 是用來修改 state 的。只能是同步操作。
actions可以理解成:我們從后端拿到數(shù)據(jù),總得做個(gè)處理。處理完了再存到數(shù)據(jù)庫中,當(dāng)然也可以不處理,就是直接mutation。包含異步操作。
State
https://vuex.vuejs.org/zh/guide/state.html
// 創(chuàng)建一個(gè) Counter 組件
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return store.state.count
}
}
}
每當(dāng) store.state.count 變化的時(shí)候, 都會(huì)重新求取計(jì)算屬性,并且觸發(fā)更新相關(guān)聯(lián)的 DOM。
然而,這種模式導(dǎo)致組件依賴全局狀態(tài)單例。在模塊化的構(gòu)建系統(tǒng)中,在每個(gè)需要使用 state 的組件中需要頻繁地導(dǎo)入,并且在測試組件時(shí)需要模擬狀態(tài)。
Vuex 通過 store 選項(xiàng),提供了一種機(jī)制將狀態(tài)從根組件“注入”到每一個(gè)子組件中
(需調(diào)用 Vue.use(Vuex)):
const app = new Vue({
el: '#app',
// 把 store 對象提供給 “store” 選項(xiàng),這可以把 store 的實(shí)例注入所有的子組件
store,
components: { Counter },
template: `
<div class="app">
<counter></counter>
</div>
`
})
通過在根實(shí)例中注冊 store 選項(xiàng),該 store 實(shí)例會(huì)注入到根組件下的所有子組件中,
且子組件能通過 this.$store 訪問到。讓我們更新下 Counter 的實(shí)現(xiàn):
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return this.$store.state.count
}
}
}
mapState: 當(dāng)一個(gè)組件需要獲取多個(gè)狀態(tài)的時(shí)候,將這些狀態(tài)都聲明為計(jì)算屬性會(huì)有些重復(fù)和冗余。為了解決這個(gè)問題,我們可以使用 mapState 輔助函數(shù)幫助我們生成計(jì)算屬性。
// 在單獨(dú)構(gòu)建的版本中輔助函數(shù)為 Vuex.mapState
import { mapState } from 'vuex'
export default {
// ...
computed: mapState({
// 箭頭函數(shù)可使代碼更簡練
count: state => state.count,
// 傳字符串參數(shù) 'count' 等同于 `state => state.count`
countAlias: 'count',
// 為了能夠使用 `this` 獲取局部狀態(tài),必須使用常規(guī)函數(shù)
countPlusLocalState (state) {
return state.count + this.localCount
}
})
}
mapState 函數(shù)返回的是一個(gè)對象。我們?nèi)绾螌⑺c局部計(jì)算屬性混合使用呢?通常,我們需要使用一個(gè)工具函數(shù)將多個(gè)對象合并為一個(gè),以使我們可以將最終對象傳給 computed 屬性。
computed: {
localComputed () { /* ... */ },
// 使用對象展開運(yùn)算符將此對象混入到外部對象中
...mapState({
// ...
})
}
Getter
當(dāng)組件多處要使用某屬性時(shí),我們可以使用getters(可以理解成store的計(jì)算屬性)得到。getter 的返回值會(huì)根據(jù)它的依賴被緩存起來,且只有當(dāng)它的依賴值發(fā)生了改變才會(huì)被重新計(jì)算。
// 通過屬性訪問
store.getters.doneTodos // -> [{ id: 1, text: '...' }]
getters: {
// ...第一個(gè)參數(shù):state, 第二個(gè)參數(shù):其他getters
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
store.getters.doneTodosCount // -> 1
注意,getter 在通過屬性訪問時(shí)是作為 Vue 的響應(yīng)式系統(tǒng)的一部分緩存其中的。
// 通過方法訪問
注意,getter 在通過方法訪問時(shí),每次都會(huì)去進(jìn)行調(diào)用,而不會(huì)緩存結(jié)果。
mapGetters輔助函數(shù)僅僅是將 store 中的 getter 映射到局部計(jì)算屬性
import { mapGetters } from 'vuex'
export default {
// ...
computed: {
// 使用對象展開運(yùn)算符將 getter 混入 computed 對象中
...mapGetters([
doneCount: 'doneTodosCount', // 起別名
'anotherGetter',
// ...
])
}
}
Mutation
更改 Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation。Vuex 中的 mutation 非常類似于事件:每個(gè) mutation 都有一個(gè)字符串的 事件類型 (type) 和 一個(gè) 回調(diào)函數(shù) (handler)。這個(gè)回調(diào)函數(shù)就是我們實(shí)際進(jìn)行狀態(tài)更改的地方,并且它會(huì)接受 state 作為第一個(gè)參數(shù)。
”要喚醒一個(gè) mutation handler,你需要以相應(yīng)的 type 調(diào)用 store.commit 方法
// 倆種提交方法
store.commit('increment', {amount: 10})
store.commit({
type: 'increment',
amount: 10
})
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
Action
Action 類似于 mutation,不同在于:
Action 提交的是 mutation,而不是直接變更狀態(tài)。
Action 可以包含任意異步操作。
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
// context 上下文對象,context.state, context.rootState
context.commit('increment')
}
}
})
// 分發(fā)Action, 調(diào)用Action
store.dispatch('increment')
// 以載荷形式分發(fā)
store.dispatch('incrementAsync', {
amount: 10
})
// 以對象形式分發(fā)
store.dispatch({
type: 'incrementAsync',
amount: 10
})
// 在組件中分發(fā) Action
this.$store.dispatch('xxx')
...mapActions([
'increment', // 將 `this.increment()` 映射為 `this.$store.dispatch('increment')`
// `mapActions` 也支持載荷:
'incrementBy' // 將 `this.incrementBy(amount)` 映射為 `this.$store.dispatch('incrementBy', amount)`
]),
組合Action
- 處理異步操作,返回Promise
actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}
store.dispatch('actionA').then(() => {
// ...
})
- 利用async/await
// 假設(shè) getData() 和 getOtherData() 返回的是 Promise
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}
一個(gè) store.dispatch 在不同模塊中可以觸發(fā)多個(gè) action 函數(shù)。在這種情況下,只有當(dāng)所有觸發(fā)函數(shù)完成后,返回的 Promise 才會(huì)執(zhí)行。
Module
Vuex 允許我們將 store 分割成模塊(module)。每個(gè)模塊擁有自己的 state、mutation、action、getter、甚至是嵌套子模塊——從上至下進(jìn)行同樣方式的分割。
state回事模塊的局部狀態(tài)對象。
對于模塊內(nèi)部的 action,局部狀態(tài)通過 context.state 暴露出來,根節(jié)點(diǎn)狀態(tài)則為 context.rootState。
getter、action都可以拿到第三個(gè)參數(shù)rootState。
命名空間:namespaced: true