天天看點

新手Vuex入門之modules最後一篇

5.modules

背景:

在Vuex中所有的狀态都放在state裡面,如果項目比較複雜,那state是一個很大的對象,store對象也将對變得非常大,難于管理。

modules:可以讓每一個子產品擁有自己的state、mutations、action、getters,使得結構非常清晰,友善管理,甚至是嵌套子子產品——從上至下進行同樣方式的分割。

尤其在多人開發同一個項目的時候,自己負責自己相關的子產品的state,會讓操作state都會變得更加直覺。

先來看下示例:

const moduleA = {
        state: {
            name:'lily',
            role:'超級管理者',
            sex:'女'
        },
        getters: {
            myProfile(state, getters, rootState, rootGetters) {
            // 這裡的 `state` 對象是子產品的局部狀态
                console.log(rootState,getters,rootState,rootGetters,'lily')
                return state.name + state.role
            }
        },
        mutations: {
            SET_ROLE(state,str) {
            // 這裡的 `state` 對象是子產品的局部狀态
                state.role = str;
            }
        },
        actions: {
            SET_ROLE (context) {
                //在 action 中,由于它接收過來的資料都被包在context對象中的,是以解出來沒有什麼順序的限制,你可以這樣:
                let  { state,commit,rootState, } = context;
                //也可以這樣:
                //let  { commit,state,rootGetters,rootState} = context;
               commit('SET_ROLE','普通管理者');
               console.log(context)
            }
        },
    }
    
    const moduleB = {
        state: {
            token:'kfep0f91265jiefj2512',
            permission:[]
        },
        getters: {
            getToken({state, getters, rootState}) {
                //console.log(state, getters, rootState)
                return state.token
            }
        },
        mutations: { },
        actions: {  }
    }
    
    
    const store = new Vuex.Store({
        state: {
            todoLists: [1,5,5,2,3,6,6,5,0,8],
        },
        mutations:{
            // 新增list
            ADDLIST(state, item) {
                state.todoLists.push(item);
                console.log(state.todoLists,'state.todoLists')
            },
        },
        actions:{
            addList(context, item) {
                context.commit('ADDLIST', item);
            },
        },
        modules: {
            a: moduleA,
            b: moduleB
        }
    })
    
    new Vue({
        el: '#app',
        data: {
            name: 'init name'
        },
        store: store,
        mounted: function() {
            //console.log(this,myStore.getters.todoCount);
        },
        computed:{
            ROLE:function() {
                this.$store.dispatch('SET_ROLE');
            },
            ...Vuex.mapGetters(['myProfile'])
        }
    })

store.state.a // -> moduleA 的state狀态
store.state.b // -> moduleB 的state狀态
    
    
           

1. 子產品的局部狀态

我們從上面這個示例中作如下分析:

1.moduleA中的mutation和getter接受到的第一個參數state是moduleA的局部狀态對象,隻屬于子產品本身所有,并且對于moduleA中的getter,根節點狀态會作為第三個參數暴露出來,這個參數可以讓我們通路 store 根節點的資料 state

const moduleA = {
        state: {
            name:'lily',
            role:'超級管理者',
            sex:'女'
        },
        getters: {
            myProfile(state, getters, rootState) {
            // 這裡的 `state` 對象是子產品的局部狀态
                console.log(rootState,getters,rootState,'lily')
                return state.name + state.role
            }
        },
        mutations: {
            SET_ROLE(state,str) {
            // 這裡的 `state` 對象是子產品的局部狀态
                state.role = str;
            }
        }
    }

           
  1. moduleA中的action局部狀态通過context.state暴露出來,根節點狀态則為context.rootState
const moduleA = {
        state: {
            name:'lily',
            role:'超級管理者',
            sex:'女'
        },
        actions: {
            SET_ROLE (context) {
               let  { state,commit,rootState, } = context;
               commit('SET_ROLE','普通管理者');
               console.log(context)
            }
        },
    }

           

2.命名空間

前面我們已經知道了,子產品内部的action、mutation和getter預設是注冊在全局命名空間的,它有以下兩個弊端:

  1. 不同子產品中有相同命名的mutations、actions時,不同子產品對同一 mutation 或 action 會作出響應。
  2. 當一個項目中store分了很多子產品的時候,在使用輔助函數mapState、mapGetters、mapMutations、mapActions時,很難查詢,引用的state、getters、mutations、actions來自于哪個子產品,不便于後期維護。

如果我們隻想讓他們在目前的子產品中生效,應該怎麼辦呢?通過添加namespaced:true 的方式使其成為帶命名空間的子產品。當子產品被注冊後,它的所有 getter、action 及 mutation 都會自動根據子產品注冊的路徑調整命名。

我們把上面的代碼添加namespaced:true來看下

const moduleA = {
        namespaced:true,
        state: {
            name:'lily',
            role:'超級管理者',
            sex:'女'
        },
        actions: {
            SET_ROLE (context) {
               let  { state,commit,rootState, } = context;
               commit('SET_ROLE','普通管理者');
               console.log(context)
            }
        },
    }

           

以上代碼會報下面這樣的錯誤:

vuex.js:423 [vuex] unknown action type: SET_ROLE

vuex.js:855 [vuex] unknown getter: myProfile
           

這是因為在全局actions已經找不到這個SET_ROLE這個事件類型了,因為他的路徑變了,不再屬于全局了,他僅僅屬于moduleA了。是以我們需要修改成這樣:

computed:{
        ROLE:function() {
            this.$store.dispatch('a/SET_ROLE');
        },
        //這裡的a是moduleA的子產品命名空間名字
        ...Vuex.mapGetters('a',[myProfile'])
    }
           

這裡需要注意一點的就是,如果一個子產品啟用了命名空間,那麼啟用了命名空間的 getter 和 action 會收到局部化的getter,dispatch和commit。換言之,你在使用子產品内容(moduleassets)時不需要在同一子產品内額外添加空間名字首。更改 namespaced 屬性後不需要修改子產品内的代碼。

2.1 那麼我們如何在帶命名空間的子產品内通路全局内容呢?

通過前面的學習,我們要重點劃線标記下:

++如果你希望使用全局 state 和 getter,rootState 和 rootGetters 會作為第三和第四參數傳入 getter,也會通過 context 對象的屬性傳入 action。++

如果我想要在帶命名空間的子產品内dispatch actions 或者 commit mutation,那麼将 { root: true } 作為第三參數傳給 dispatch 或 commit 即可。來看下面的示例:

const moduleA = {
        namespaced:true,
        state: {
            name:'lily',
            role:'超級管理者',
            sex:'女'
        },
        actions: {
            SET_ROLE (context) {
               let  { state,commit,rootState, } = context;
               commit('SET_ROLE','普通管理者');
               //commit('ADDLIST','huanle',{root:true})
               dispatch('addList','lily',{root:true})
               console.log(context)
            }
        },
    }

           

通過上面的代碼,todoLists裡面多了個lily這項,說明我們從子產品内部分發全局的actions已經成功。

2.2如何在帶命名空間的子產品内注冊全局 action

若需要在帶命名空間的子產品注冊全局 action,你可添加 root: true,并将這個 action 的定義放在函數 handler 中。

const moduleA = {
        namespaced:true,
        state: {
            name:'lily',
            role:'超級管理者',
            sex:'女'
        },
        actions: {
            //SET_ROLE (context) {
               //let  { state,commit,rootState, } = context;
               //commit('SET_ROLE','普通管理者');
               //commit('ADDLIST','huanle',{root:true})
               //dispatch('addList','lily',{root:true})
               //console.log(context)
            //},
            //若需要在帶命名空間的子產品注冊全局 action,你可添加 root: true,并将這個 action 的定義放在函數 handler 中
            SET_ROLE: {
                root:true,
                handler (namespacedContext, payload) {
                    let  { state,commit,rootState,dispatch } = namespacedContext;
                    commit('SET_ROLE','普通管理者');
                    commit('ADDLIST','huanle',{root:true})
                    //dispatch('addList','lily',{root:true})
                    console.log(state,rootState,'在帶命名空間的子產品注冊全局 action已經成功')
                }
            }
        },
    }

           

簡單解釋下,這裡的 namespacedContext 就相當于目前子產品的上下文對象,payload 是調用的時候所傳入的參數,當然也叫載荷。

示例就講到這裡吧,接下來繼續學習下一要點。

3.帶命名空間的綁定函數

當使用 mapState, mapGetters, mapActions 和 mapMutations 這些函數來綁定帶命名空間的子產品時,寫起來可能比較繁瑣:先看下這裡的示例,這是第一種方式:

computed:{
        ROLE:function() {
            this.$store.dispatch('a/SET_ROLE');
        },
        //這裡的a是moduleA的子產品命名空間名字
        ...Vuex.mapGetters({
            myProfile:'a/myProfile'
        })
    }
           

對于這種情況,你可以将子產品的空間名稱字元串作為第一個參數傳遞給上述函數,這樣所有綁定都會自動将該子產品作為上下文。于是上面的例子可以簡化為:

computed:{
        ROLE:function() {
            this.$store.dispatch('a/SET_ROLE');
        },
        //這裡的a是moduleA的子產品命名空間名字
        ...Vuex.mapGetters('a',[myProfile'])
    }
           

第二種方式:

你可以通過使用createNamespacedHelpers建立基于某個命名空間輔助函數。它傳回一個對象,對象裡有新的綁定在給定命名空間值上的元件綁定輔助函數:

這裡我就直接引用官方的例子了:

import { createNamespacedHelpers } from 'vuex'

const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')

export default {
  computed: {
    // 在 `some/nested/module` 中查找
    ...mapState({
      a: state => state.a,
      b: state => state.b
    })
  },
  methods: {
    // 在 `some/nested/module` 中查找
    ...mapActions([
      'foo',
      'bar'
    ])
  }
}

           

4.子產品動态注冊

哈哈。這部分内容各位看官就去官方文檔翻翻看吧,因為部落客覺得動态子產品一般在業務項目中用得特别少,幾乎用不到,是以隻學項目中需要用到的,但有可能會在第三方庫中,類似封裝好的一些架構中用到這些,是以大家有需求需要動态添加子產品時,去看下官方文檔就好,是以這裡也不贅述了。謝謝大家。

寫在最後

至此,Vuex 的核心概念就已經全部講完了。雖然這套架構有點難,但其實隻要我們花點心思在上面,也是能弄懂的。大家一定要在項目中多運用,多實操。