vuex中幾個(gè)核心概念: state, getters, mutations, actions, module
getters
可以認(rèn)為是store的計(jì)算屬性;與計(jì)算屬性一樣,getter的返回值會(huì)根據(jù)它的依賴(lài)緩存起來(lái),且只有當(dāng)它的依賴(lài)值發(fā)生變化才會(huì)被重新計(jì)算
mapGetters
輔助函數(shù)僅僅是將 store 中的 getter 映射到局部計(jì)算屬性:
import { mapGetters } from 'vuex'
export default {
// ...
computed: {
// 使用對(duì)象展開(kāi)運(yùn)算符將 getter 混入 computed 對(duì)象中
...mapGetters([
'doneTodosCount',
'anotherGetter',
// ...
])
}
}
mutations 只能是同步操作
更改vuex的store中的狀態(tài)的唯一方法就是提交 mutations
在 mutation 中混合異步調(diào)用會(huì)導(dǎo)致你的程序很難調(diào)試。例如,當(dāng)你能調(diào)用了兩個(gè)包含異步回調(diào)的 mutation 來(lái)改變狀態(tài),你怎么知道什么時(shí)候回調(diào)和哪個(gè)先回調(diào)呢?
mutation必須是同步函數(shù)
mutations: {
someMutation (state) {
api.callAsyncMethod(() => {
state.count++
})
}
}
現(xiàn)在想象,我們正在 debug 一個(gè) app 并且觀(guān)察 devtool 中的 mutation 日志。每一條 mutation 被記錄,devtools 都需要捕捉到前一狀態(tài)和后一狀態(tài)的快照。然而,在上面的例子中 mutation 中的異步函數(shù)中的回調(diào)讓這不可能完成:因?yàn)楫?dāng) mutation 觸發(fā)的時(shí)候,回調(diào)函數(shù)還沒(méi)有被調(diào)用,devtools 不知道什么時(shí)候回調(diào)函數(shù)實(shí)際上被調(diào)用——實(shí)質(zhì)上任何在回調(diào)函數(shù)中進(jìn)行的狀態(tài)的改變都是不可追蹤的。
在組件中提交mutations
你可以在組件中使用 this.$store.commit('xxx') 提交 mutation,或者使用 mapMutations 輔助函數(shù)將組件中的 methods 映射為 store.commit 調(diào)用(需要在根節(jié)點(diǎn)注入 store)。
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapMutations([
'increment', // 將 `this.increment()` 映射為 `this.$store.commit('increment')`
// `mapMutations` 也支持載荷:
'incrementBy' // 將 `this.incrementBy(amount)` 映射為 `this.$store.commit('incrementBy', amount)`
]),
...mapMutations({
add: 'increment' // 將 `this.add()` 映射為 `this.$store.commit('increment')`
})
}
}
actions 可以是異步操作
- action提交的是mutation,而不是直接更改狀態(tài)
- action 可以包含任何異步操作
分發(fā) action
在組件中分發(fā)Action
你在組件中使用 this.$store.dispatch('xxx') 分發(fā) action,或者使用 mapActions 輔助函數(shù)將組件的 methods 映射為 store.dispatch 調(diào)用(需要先在根節(jié)點(diǎn)注入 store):
import { mapActions } from 'vuex'
export default {
// ...
methods: {
...mapActions([
'increment', // 將 `this.increment()` 映射為 `this.$store.dispatch('increment')`
// `mapActions` 也支持載荷:
'incrementBy' // 將 `this.incrementBy(amount)` 映射為 `this.$store.dispatch('incrementBy', amount)`
]),
...mapActions({
add: 'increment' // 將 `this.add()` 映射為 `this.$store.dispatch('increment')`
})
}
}
組合 Action
Action 通常是異步的,那么如何知道 action 什么時(shí)候結(jié)束呢?更重要的是,我們?nèi)绾尾拍芙M合多個(gè) action,以處理更加復(fù)雜的異步流程?
首先,你需要明白 store.dispatch 可以處理被觸發(fā)的 action 的處理函數(shù)返回的 Promise,并且 store.dispatch 仍舊返回 Promise:
// 假設(shè) getData() 和 getOtherData() 返回的是 Promise
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}
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)
如果你希望使用全局 state 和 getter,rootState 和 rootGetter 會(huì)作為第三和第四參數(shù)傳入 getter,也會(huì)通過(guò) context 對(duì)象的屬性傳入 action。
命名空間
默認(rèn)情況下,模塊內(nèi)部的 action、mutation 和 getter 是注冊(cè)在全局命名空間的——這樣使得多個(gè)模塊能夠?qū)ν?mutation 或 action 作出響應(yīng)。
如果希望你的模塊具有更高的封裝度和復(fù)用性,你可以通過(guò)添加 namespaced: true 的方式使其成為命名空間模塊。當(dāng)模塊被注冊(cè)后,它的所有 getter、action 及 mutation 都會(huì)自動(dòng)根據(jù)模塊注冊(cè)的路徑調(diào)整命名。例如:
const store = new Vuex.Store({
modules: {
account: {
namespaced: true,
// 模塊內(nèi)容(module assets)
state: { ... }, // 模塊內(nèi)的狀態(tài)已經(jīng)是嵌套的了,使用 `namespaced` 屬性不會(huì)對(duì)其產(chǎn)生影響
getters: {
isAdmin () { ... } // -> getters['account/isAdmin']
},
actions: {
login () { ... } // -> dispatch('account/login')
},
mutations: {
login () { ... } // -> commit('account/login')
},
// 嵌套模塊
modules: {
// 繼承父模塊的命名空間
myPage: {
state: { ... },
getters: {
profile () { ... } // -> getters['account/profile']
}
},
// 進(jìn)一步嵌套命名空間
posts: {
namespaced: true,
state: { ... },
getters: {
popular () { ... } // -> getters['account/posts/popular']
}
}
}
}
}
})
如果你希望使用全局 state 和 getter,rootState 和 rootGetter 會(huì)作為第三和第四參數(shù)傳入 getter,也會(huì)通過(guò) context 對(duì)象的屬性傳入 action。
若需要在全局命名空間內(nèi)分發(fā) action 或提交 mutation,將 { root: true } 作為第三參數(shù)傳給 dispatch 或 commit 即可。
需要注意的點(diǎn)
1.默認(rèn)情況下,模塊內(nèi)的getter, mutation,action是注冊(cè)在全局空間的,state只注冊(cè)在局部命名空間的;
要想使模塊內(nèi)的getter, mutation,action注冊(cè)在模塊命名空間,必須在模塊內(nèi)加上 namespaced: true


使用命名空間在調(diào)用action時(shí)必須使用
this.$store.dispatch('hero1/getHeroInfo');
computed: {
doneTodosCount () {
return this.$store.getters['hero1/doneTodos'][0].item;
}
},
2.頁(yè)面刷新時(shí),store中的數(shù)據(jù)會(huì)清空
解決方案
https://stackoverflow.com/questions/43027499/vuex-state-on-page-refresh
3.雙向綁定(v-model)和 vuex 是否沖突
<template>
<div >
<input type="text" v-model="obj.message">
</div>
</template>
<script>
export default {
computed: {
obj() {
return this.$store.state.test.obj;
},
},
}
</script>
test.js
import Vue from 'vue';
const test = {
state: {
obj: {}
},
mutations: {
updateMessage(state, message) {
state.obj = { message };
}
},
}
export default test;
以上代碼在嚴(yán)格模式下會(huì)報(bào)錯(cuò)
vuex開(kāi)啟嚴(yán)格模式, 僅需要在創(chuàng)建store的時(shí)候傳入 strict: true
const store = new Vuex.Store({
// ...
strict: true
})
在嚴(yán)格模式下, 無(wú)論何時(shí)發(fā)生了狀態(tài)變更且且不是由mutation函數(shù)引起的, 經(jīng)會(huì)拋出錯(cuò)誤, 這能保證所有的狀態(tài)變更都能被調(diào)試工具跟蹤到;
那我們應(yīng)該怎么處理呢, vuex的官方文檔中給給了解決方法: 表單處理
- 給 <input> 中綁定 value,然后偵聽(tīng) input 或者 change 事件,在事件回調(diào)中調(diào)用 action:
<template>
<div >
<input type="text" :value="message" @input="updateMessage">
</div>
</template>
<script>
export default {
computed: {
message(){
return this.$store.state.test.obj.message;
},
},
methods: {
updateMessage(e) {
this.$store.commit('updateMessage', e.target.value)
},
}
}
</script>
- 雙向綁定的計(jì)算屬性: v-model + 使用帶有 setter 的雙向綁定計(jì)算屬性
<template>
<div >
<input type="text" v-model="message">
</div>
</template>
<script>
export default {
computed: {
message: {
get() {
return this.$store.state.test.obj.message;
},
set(value) {
this.$store.commit('updateMessage', value)
}
},
},
}
</script>