新手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 的核心概念就已经全部讲完了。虽然这套框架有点难,但其实只要我们花点心思在上面,也是能弄懂的。大家一定要在项目中多运用,多实操。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值