作者:小土豆
掘金:https://juejin.im/user/58c61b4361ff4b005d9e894d
微信公眾號:不知名寶藏程序媛(關注"不知名寶藏程序媛"免費領取前端電子書籍。文章公眾號首發,關注公眾號第一時間獲取最新文章。)
碼字不易,點贊鼓勵喲~
一.前言
上一篇文章《Vuex入門實踐(上)》,我們一共實踐了vuex的這些內容:
1.在state中定義共享屬性,在組件中可使用[$store.state.屬性名]訪問共享屬性
2.在mutations可中定義修改共享數據的方法,在組件中可使用[$store.commit('方法名')]同步修改共享屬性
3.在actions中可定義異步修改共享數據的方法,在組件中可使用[$store.dispatch('方法名')]異步修改共享屬性
4.在getters中可定義共享數據的計算屬性,在組件中可使用[$store.getters.計算屬性名]訪問共享數據的計算屬性
這篇文章接着實踐和探究vuex的奧秘。
二.創建多個store模塊
前面的文章中,我們在vuex的實例方法Store上分別創建了state、mutations、actions和getters這幾個配置選項。
可以思考一下,當我們的應用程序愈加復雜時,組件之間需要共享的數據也在持續增加,那么state、mutations、actions和getters配置項里面的代碼會愈加龐大。
因此為解決此問題,vuex支持每個模塊定義自己的state、mutations、actions和getters。

1.多個Module配置
現在我們來實踐一下多個module。首先我們在E:\MyStudy\test\VueDemo\src\vuex目錄下新建兩個文件:moduleA.js和moduleB.js。

現在,我們分別編輯這兩個文件的state、mutations、actions和getters配置項。
E:\MyStudy\test\VueDemo\src\vuex\moduleA.js
const moduleA = { state:{ counter: 100 }, mutations: { //遞增
increase(state) { state.counter++ }, //遞減
decrement(state) { state.counter-- } }, actions: { increaseAction(context) { setTimeout(function(){ //action通過提交mutation改變共享數據狀態
context.commit('increase'); },3000) }, decrementAction(context){ setTimeout(function(){ //action通過提交mutation改變共享數據狀態
context.commit('decrement'); },3000) } }, getters: { doubleCounter(state) { return state.counter*state.counter } } } export default moduleA
E:\MyStudy\test\VueDemo\src\vuex\moduleB.js
const moduleB = { state:{ counter: 5 }, mutations: { //遞增
increase(state) { state.counter++ }, //遞減
decrement(state) { state.counter-- } }, actions: { increaseAction(context) { setTimeout(function(){ //action通過提交mutation改變共享數據狀態
context.commit('increase'); },3000) }, decrementAction(context){ setTimeout(function(){ //action通過提交mutation改變共享數據狀態
context.commit('decrement'); },3000) } }, getters: { doubleCounter(state){ return state.counter*state.counter } } } export default moduleB
可以看到每個module定義自己的state、mutations、actions和getters和直接寫在store.js中的語法相差無幾,只是單個module無需將這些選項掛載到vuex實例的Store方法上。
接着我們在store.js中編寫該模塊的state、mutations、actions和getters,並且配置這兩個module
E:\MyStudy\test\VueDemo\src\vuex\store.js
import Vue from 'vue' import Vuex from 'vuex' import moduleA from './moduleA' import moduleB from './moduleB' Vue.use(Vuex) export default new Vuex.Store({ state: { counter: 1000 }, mutations: { //遞增
increase(state) { state.counter++ }, //遞減
decrement(state) { state.counter-- } }, actions: { increaseAction(context) { setTimeout(function(){ //action通過提交mutation改變共享數據狀態
context.commit('increase'); },3000) }, decrementAction(context){ setTimeout(function(){ //action通過提交mutation改變共享數據狀態
context.commit('decrement'); },3000) } }, getters: { doubleCounter(state) { return state.counter*state.counter } }, modules: { a: moduleA, b: moduleB } })
store.js中對多個module的配置語法也比較簡單,即在modules中以字典的形式【模塊名:模塊引用】寫入即可。其余配置state、mutations、actions和getters的方法和前面的一模一樣。
vuex多個module的配置完成,現在我們分別按順序訪問store模塊、a模塊和b模塊的共享數據、同步/異步修改共享數據以及計算屬性訪問。
2.多個Module-共享數據訪問
多個Module這種情況下,state中共享數據的訪問被綁定在模塊上(模塊內部的state是局部的,只屬於模塊本身),因此我們需要使用【$store.state.模塊名稱】去訪問不同模塊的共享數據。
而對於store.js這個根模塊配置的state數據,直接使用【$store.state】訪問即可。
那么訪問store根模塊counter的邏輯:$store.state.counter;訪問a模塊counter的邏輯為:$store.state.a.counter;訪問b模塊的counter的邏輯為:$store.state.b.counter。
現在我們分別在App.vue組件中訪問store.js這個根模塊的counter和a模塊的counter數據,在Index.vue組件中訪問b模塊的counter數據。
(在那個組件中訪問那個模塊的state無所謂,也可以舍棄Index.vue,將所有代碼都寫在App.vue組件中)
E:\MyStudy\test\VueDemo\src\App.vue
<template>
<div id="app">
<img src="./assets/logo.png">
<!-- 獲取共享數據 -->
<h1>這里是App組件</h1>
<h3> App組件獲取共享數據 </h3>
<h3>訪問根模塊counter——$store.state.counter : {{ $store.state.counter }} </h3>
<h3>訪問a模塊counter——$store.state.a.counter : {{ $store.state.a.counter }} </h3>
<hr/>
<Index></Index>
</div>
</template>
<script> import Index from './components/Index' export default { name: 'App', components: { Index } } </script>
<style> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px;
}
</style>
E:\MyStudy\test\VueDemo\src\components\Index.vue
<template>
<div>
<h1>這里是Index.vue組件</h1>
<h3>Index組件獲取共享數據 </h3>
<h3>訪問b模塊counter——$store.state.b.counter :{{ $store.state.b.counter }}</h3>
</div>
</template>
<script> export default { name: 'Index' } </script>
最后我們啟動項目查看一下結果

可以看到,我們已經成功的訪問到了根模塊的counter數據、a模塊的counter數據以及b模塊的counter數據。
3.多個Module-同步修改共享數據
現在,我們需要在App.vue組件中同步修改store根模塊的數據和a模塊的數據,在Index.vue組件中同步修改b模塊的數據。
前面我們說了state綁定在模塊上,那mutations它並沒有和模塊綁定。
那么根據《vuex入門實踐(上)》這一篇文章中的總結,能想到觸發counter遞增的方法為$store.commit(‘increase’)。
由於根store模塊、a模塊、b模塊中使counter遞增和遞減的mutation方法名都是一模一樣的,那會出現什么樣的結果呢?
我們來試一下。
首先在根store模塊、a模塊、b模塊中的mutation increase中添加打印console.log的打印信息,其余代碼沿用前面的保存不變
E:\MyStudy\test\VueDemo\src\vuex\store.js
//遞增
increase(state) { console.log("store-increase") state.counter++ }
E:\MyStudy\test\VueDemo\src\vuex\moduleA.js
//遞增
increase(state) { console.log("moduleA-increase") state.counter++ },
E:\MyStudy\test\VueDemo\src\vuex\moduleB.js
//遞增
increase(state) { console.log("moduleB-increase") state.counter++ },
接着編寫App.vue和Index.vue的代碼
E:\MyStudy\test\VueDemo\src\App.vue
<template>
<div id="app">
<img src="./assets/logo.png">
<!-- 獲取共享數據 -->
<h1>這里是App組件</h1>
<h3> App組件獲取共享數據 </h3>
<h3>訪問根模塊counter——$store.state.counter : {{ $store.state.counter }} </h3>
<h3>訪問a模塊counter——$store.state.a.counter : {{ $store.state.a.counter }} </h3>
<h3>同步修改根模塊counter:<button @click="$store.commit('increase')">同步修改根模塊counter</button></h3>
<h3>同步修改a模塊counter:<button @click="$store.commit('increase')">同步修改a模塊counter</button></h3>
<hr/>
<Index></Index>
</div>
</template>
<script> import Index from './components/Index' export default { name: 'App', components: { Index } } </script>
<style> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px;
}
</style>
E:\MyStudy\test\VueDemo\src\components\Index.vue
<template>
<div>
<h1>這里是Index.vue組件</h1>
<h3>Index組件獲取共享數據 </h3>
<h3>訪問b模塊counter——$store.state.b.counter :{{ $store.state.b.counter }}</h3>
<h3>同步修改b模塊counter:<button @click="$store.commit('increase')">同步修改b模塊counter</button></h3>
</div>
</template>
<script> export default { name: 'Index' } </script>
最后我們來看下效果

那么現象就是,執行一次$store.commit('increase')會依次觸發store根模塊、a模塊和b模塊mutation中increase的執行。
所以看到當點擊button時這三個模塊的counter都會加1。
關於觸發increase的順序呢,先是根模塊,然后是根據根模塊中modules的配置順序決定的。
這個操作呢只是我自己的一個小嘗試,真正多模塊的mutation也不會這樣去使用。
那么想避免上面問題的出現,我們可以避免多個模塊中的mutation方法的重名。
我們來實踐一下,store模塊中mutation的increase名稱不做修改,只修改a、b這兩個模塊。
E:\MyStudy\test\VueDemo\src\vuex\moduleA.js
mutations: { //遞增
increaseA(state) { console.log("moduleA-increase") state.counter++ } },
E:\MyStudy\test\VueDemo\src\vuex\moduleB.js
mutations: { //遞增
increaseB(state) { console.log("moduleB-increase") state.counter++ } },
然后對應的需要修改App.vue和Index.vue中觸發mutation的代碼(只貼出修改部分代碼)
E:\MyStudy\test\VueDemo\src\App.vue
<h3>同步修改a模塊counter:<button @click="$store.commit('increaseA')">同步修改a模塊counter</button></h3>
E:\MyStudy\test\VueDemo\src\components\Index.vue
<h3>同步修改b模塊counter:<button @click="$store.commit('increaseB')">同步修改b模塊counter</button></h3>
現在在看下效果

可以看得到不同的按鈕觸發了不同的模塊的mutation。
那么還有一個問題,當module特別多時,其實不可避免真的會有重名的mutation,那么此時vuex的命令命名空間就需要上場了。
命名空間也很簡單,就是在模塊中添加一個配置namespaced:true。
現在我們來實踐一下分別給模塊a和模塊b添加命名空間的配置,同時在把mutation中遞增的方法名稱統一改回increase。
E:\MyStudy\test\VueDemo\src\vuex\moduleA.js
const moduleA = { namespaced: true, state:{ counter: 100 }, mutations: { //遞增 increase(state) { console.log("moduleA-increase") state.counter++ }, //遞減 decrement(state) { state.counter-- } }, actions: { increaseAction(context) { setTimeout(function(){ //action通過提交mutation改變共享數據狀態 context.commit('increase'); },3000) }, decrementAction(context){ setTimeout(function(){ //action通過提交mutation改變共享數據狀態 context.commit('decrement'); },3000) } }, getters: { doubleCounter(state) { return state.counter*state.counter } } } export default moduleA
E:\MyStudy\test\VueDemo\src\vuex\moduleB.js
const moduleB = { namespaced: true, state:{ counter: 5 }, mutations: { //遞增 increase(state) { console.log("moduleB-increase") state.counter++ }, //遞減 decrement(state) { state.counter-- } }, actions: { increaseAction(context) { setTimeout(function(){ //action通過提交mutation改變共享數據狀態 context.commit('increase'); },3000) }, decrementAction(context){ setTimeout(function(){ //action通過提交mutation改變共享數據狀態 context.commit('decrement'); },3000) } }, getters: { doubleCounter(state){ return state.counter*state.counter } } } export default moduleB
當有了命令空間以后,觸發mutation的方法也就有了變化: $store.commit('模塊名/方法')
E:\MyStudy\test\VueDemo\src\App.vue
<h3>同步修改根模塊counter:<button @click="$store.commit('increase')">同步修改根模塊counter</button></h3> <h3>同步修改a模塊counter:<button @click="$store.commit('a/increase')">同步修改a模塊counter</button></h3>
E:\MyStudy\test\VueDemo\src\components\Index.vue
<h3>同步修改b模塊counter:<button @click="$store.commit('b/increase')">同步修改b模塊counter</button></h3>
看下結果

可以看到命名空間的效果和前面修改mutation名稱不重復是同樣的效果。
那么關於多模塊的mutation觸發就總結完了。
4.多個Module-異步修改共享數據
異步修改共享數據的邏輯和前面同步修改的相同。
我們在actions中定義了異步同名的遞增和遞減方法,執行一次$store.dispatch('increaseAction'),會依次觸發執行store、a模塊和b模塊的actions。
同樣我們可以選擇讓不同模塊的actions方法名稱不重復,也可以使用命名空間去解決。
這里我們只演示命名空間的方式去觸發不同module的actions。
store.js、moduleA.js和moduleB.js的代碼沿用上一小節的不做修改。
只需要在App.vue和Index.vue中添加$store.dispatch('increaseAction')的邏輯即可。
E:\MyStudy\test\VueDemo\src\App.vue
<h3>異步修改根模塊counter:<button @click="$store.dispatch('increaseAction')">異步修改根模塊counter</button></h3> <h3>異步修改a模塊counter:<button @click="$store.dispatch('a/increaseAction')">異步修改a模塊counter</button></h3>
E:\MyStudy\test\VueDemo\src\components\Index.vue
<h3>異步修改b模塊counter:<button @click="$store.dispatch('b/increaseAction')">異步修改b模塊counter</button></h3>
現在我們看下結果

可以看到已經成功的觸發了不同模塊的actions。
5.多個Module-計算屬性訪問
最后一部分就是多module的getters訪問了。
首先這個需要說明的是getters也沒有和模塊進行綁定,在我們沒有給a和b模塊添加命名空間namespaced:true的配置前。
因為多個模塊的getters存在重名屬性,因此控制台可以看到一個錯誤信息。

后面我們在moduleA.js和moduleB.js中添加了命令空間的配置后該錯誤就不會在出現。
我自己也測試了一下,同樣可以像前面那樣,保證getters中的屬性不重名,直接使用[$store.getters.屬性名]去訪問不同模塊的getters。
下面還是來實踐一下。
store.js中getters的屬性名不做修改,依然是doubleCounter。
將moduleA.js中getters的屬性名改為doubleCounterA;
將moduleB.js中getters的屬性名改為doubleCounterB;
E:\MyStudy\test\VueDemo\src\vuex\moduleA.js
getters: { doubleCounterA(state) { return state.counter*state.counter } }
E:\MyStudy\test\VueDemo\src\vuex\moduleB.js
getters: { doubleCounterB(state) { return state.counter*state.counter } }
接着就是在App.vue和Index.vue中訪問store模塊、a模塊和b模塊的計算屬性。
E:\MyStudy\test\VueDemo\src\App.vue
<h3>訪問根模塊getters——$store.getters.doubleCounter : {{ $store.getters.doubleCounter }} </h3> <h3>訪問a模塊getters——$store.getters.doubleCounterA : {{ $store.getters.doubleCounterA }} </h3>
E:\MyStudy\test\VueDemo\src\components\Index.vue
<h3>訪問b模塊getters——$store.getters.doubleCounterB : {{ $store.getters.doubleCounterB }} </h3>
瀏覽器查看結果:

可以看到已經成功的訪問到不同模塊的getters。
那么最后我們在嘗試將a、b兩個模塊的getters屬性名稱在改回doubleCounter,使用命名空間的方式去訪問。
這里不貼moduleA.js和moudleB.js的代碼了,直接修改App.vue和Index.vue中使用命名控件訪問getters的代碼
E:\MyStudy\test\VueDemo\src\App.vue
<h3>訪問根模塊getters——$store.getters.doubleCounter : {{ $store.getters.doubleCounter }} </h3>
<h3>訪問a模塊getters——$store.getters['a/doubleCounter'] : {{ $store.getters['a/doubleCounter'] }} </h3>
E:\MyStudy\test\VueDemo\src\components\Index.vue
<h3>訪問b模塊getters——$store.getters[''b/doubleCounter'] : {{ $store.getters['b/doubleCounter'] }} </h3>
瀏覽器查看結果

可以看到命名空間訪問成功。
注意:我們一直訪問getters的邏輯代碼為$store.getters.doubleCounter。
因此在嘗試使用命名空間訪問getters時我的代碼為$store.getters.a.doubleCounter。
但是發現這種方法會報錯,因此靈機一動把代碼換成了$store.getters['a/doubleCounter'],最后訪問成功。
三.總結
到此本篇文章要總結的內容就完成了。
篇幅比較長,但是也很簡單。
vuex的前一篇文章和本篇文章,在訪問state、mutations、actions和getters時均采用的是$store對象訪問,因此下一篇文章將會介紹另外一種比較常用的訪問方式。
關注博主不迷路,下次見
作者:小土豆
掘金:https://juejin.im/user/58c61b4361ff4b005d9e894d
微信公眾號:不知名寶藏程序媛(關注"不知名寶藏程序媛"免費領取前端電子書籍。文章公眾號首發,關注公眾號第一時間獲取最新文章。)
碼字不易,點贊鼓勵喲~

