Vuex實踐(中)-多module中的state、mutations、actions和getters


 

作者:小土豆

博客園:www.cnblogs.com/HouJiao/

掘金: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對象訪問,因此下一篇文章將會介紹另外一種比較常用的訪問方式。

  關注博主不迷路,下次見

 

 

作者:小土豆

博客園:www.cnblogs.com/HouJiao/

掘金:https://juejin.im/user/58c61b4361ff4b005d9e894d

微信公眾號:不知名寶藏程序媛(關注"不知名寶藏程序媛"免費領取前端電子書籍。文章公眾號首發,關注公眾號第一時間獲取最新文章。)

碼字不易,點贊鼓勵喲~

 



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM