六、Vuex - Module

Module 模块

Vuex 允许将 store 分割成模块(module), 每个模块拥有自己的state、mutation、action、getter甚至是嵌套子模块, 从上至下进行同样方式的分割。分割的好处是让代码更加清晰, 易于维护管理.

模块划分及访问

// A 模块
const moduleA = {
    state: {},
    getters: {},
    mutations: {},
    actions: {}
}

// B 模块
const moduleB = {
    state: {},
    getters: {},
    mutations: {},
    actions: {}
}

const store = new Vuex.Store({
    modules: {
        a: moduleA,
        b: moduleB
    }
});

// 访问独立模块的状态
store.state.a // 访问 moduleA 的状态
store.state.b // 访问 moduleB 的状态

模块的局部状态

  • 模块内部的 mutation、getter 的第一个参数为模块的局部状态对象
  • 模块内部的 action中, 局部状态通过 context.state 暴露, 根节点点状则为 content.rootState
  • 模块内部的 getter, 根节点的状态会作为第三个参数暴露出来
const moduleA = {
    getters: {
        getCount (state, getters, rootState) {
            // state 局部状态
            // 局部 getters, 
            // rootState 根节点状态
            return state.count + rootState.count;
        }
    },
    mutations: {
        increment (state) {
            state.count++;
            // state 模块的局部状态
        }
    },
    actions: {
        increment ({ state, commit, rootState }) {
            // state 局部状态
            // rootState 根节点状态
            commit('increment');
        }
    }
}

命名空间

默认情况下, 模块内部的 action、mutation、getter 是注册在全局命名空间的, 这样使得多个模块能够对同一mutation或action作出响应

如果希望你的模块具有更高的封装和复用性, 你可以通过天剑 namespaced: true 的方式使其成为带命名空间的模块。
当模块被注册后, 所有 getter、action及mutation 都会自动根据模块注册的路径调整命名。

const store = new Vuex.Store({
    modules: {
        account: {
            namespaced: true,

            // 模块内容
            sstate: {},
            getters: {
                inAdmin () {} // getters['account/isAdmin']
            },
            mutations: {
                login () {} // commit('account/login')
            },
            actions: {
                login () {} // dispatch('account/login')
            },

            // 嵌套模块
            modules: {
                // 继承父模块的命名空间
                myPage: {
                    state: {},
                    getters: {
                        profile () {} // getter['account/profile']
                    }
                },
                // 进一步嵌套命名空间
                posts: {
                    namespaced: true,
                    state: {},
                    getters: {
                        popular () {} // getter['account/posts/popular']
                    }
                }
            }
        }
    }
});

在带命名空间的模块内访问全局内容

  • 使用全局的 state 和 getter, rootState 和 rootGetters 会作为第三和第四参数传入 getter, 也会通过 context 对象的属性传入action
  • 分发和提交全局命名空间的action、mutation, 将 { root: true } 作为第三参数传给 dispatch 或 commit 即可
modules: {
    foo: {
        namespaed: true, 
        getters: {
            someGetter (state, getters, rootState, rootGetters) {
                getters.someOther // 'foo/someOther'
                rootGetters.someOhter // 'someOther'
            }
        },
        actions: {
            someAction ({ dispatch, commit, getters, rootGetters }) {
                dispatch('someOtherAction') // 'foo/someOhterAction'
                dispatch('someOtherAction', null, { root: true }) // 'someOhterAction' 派发根节点的 action

                commit('someMutation') // 'foo/someMutation'
                commit('someMutation', null, { root: true }) // 'someMutation' 提交根节点的 mutation
            }
        }
    }
}

在命名空间模块中注册全局 action

action: {
    someAction: {
        root: true, // 将注册到全局中
        handler () {}
    }
}

带命名空间的模块如何使用

当使用 mapState、mapGetters、mapActions、mapMutations时需要注意

// 方式一: 统一编写
computed: {
    ...mapState({
        a: state => state.some.nested.module.a,
        b: state => state.some.nested.module.b
    })
},
methods: {
    ...mapActions([
        // this['some/nested/module/foo']()
        'some/nested/module/foo',
        'some/nested/module/bar'
    ])
}

// 方式二: 将模块的空间名称作为第一个参数,自动绑定模块上下文
computed: {
    ...mapState('some/nested/module', {
        a: state => state.a,
        b: state => state.b
    })
},
methods: {
    ...mapActions('some/nested/module', {
        'foo', // this.foo()
        'bar'  // this.bar()
    })
}

// 方式三: 通过 createNamespacedHelpers 创建基于某个命名空间辅助函数
import { createNamespacedHelpers } from 'vuex'
const { mapState, mapAction } = createNamespacedHelpers('some/nested/module');

export default {
    computed: {
        ...mapState({
            a: state => state.a,
            b: state => state.b
        })
    },
    methods: {
        ...mapActions([
            'foo',
            'bar'
        ])
    }
}

相关推荐