前言 上回我们说了一下 vuex 的简单使用,最后面的时候有说了,由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割,今天我们也来简单了解一下他的使用,深入学习可能还是要去看官方文档
1 文件结构
文件结构的话,模块化的使用要多一个 modules 的文件夹,里面放着细分模块的 js 文件/模块名文件夹。
这里官方的标准是一个模块一个 js 文件,但是要是模块太复杂的话,也可以把里面的代码拆分出来。
// store 文件夹 │ actions.js │ getters.js │ index.js │ mutations.js │ state.js │ └─modules │ moduleB.js │ └─moduleA index.js mutation.js state.js
然后在创建 store 的 js 文件中引入这些模块,直接
import moduleA from './modules/moduleA/index' import moduleB from './modules/moduleB'; export default new Vuex.Store({ state, getters, mutations, actions, modules: { moduleA, moduleB, } });
2 模块的局部状态对象的定义
模块内部的 getter,mutation 和 action,他们方法接收的参数会和根状态的不一样,我们一个一个来
getter
getter 的话,他会有三个参数,第一个是模块内的 state,第二个是 模块内的 getters,第三个是根节点状态 rootState,
const getters = { bFullName: (state, getters, rootState) => `full${state.bName}` }
mutation
mutation 里面的回调函数传入的第一个参数也是 模块内的 state,其他和根状态定义的时候一样
const mutations = { // 这里的 `state` 对象是模块的局部状态 SET_B_NAME(state, payload) { debugger state.bName = payload.name; } }
action
最后的 action 的话,他传入还是只有 context 对象,然后咧,这个对象里面的 state 属性指模块内的状态,rootState 指根状态,如下
const actions = { ASYNC_SET_NAME({ state, commit, rootState }, payload) { setTimeout(() => { state.bName = 'asyncName' }, 4000) } }
3 使用
3.1 state 获取
这个的话要在原来状态名前面加一个模块名才能放到到模块内的对象。具体如下
// 原先的基础上加个模块名 this.$store.state.moduleB.bName; // 辅助函数也一样,name 前面加个模块名 Deno ...mapState({ name: state => state.moduleB.bName, })
3.2 命名空间
getter,mutation,action 他们默认都是注册在全局命名空间的,所以我们默认是可以和使用根状态一样去使用他们,但是这样不可避免会出现命名冲突的问题,所以使模块有更高的封装性与复用性,我们可以通过添加 `
namespaced: true` 使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。
// moduleB 模块导出的时候加个 namespaced: true, export default { namespaced: true, state, getters, mutations, actions, }
3.2.1 辅助函数的使用
因为有了命名空间这么一层封装,所以我们在用辅助函数的时候都要多加那么一层模块名,具体看下面代码。
// getter this.$store.getters['moduleB/bFullName']; ...mapGetters({ bGetter2: 'moduleB/bFullName' }) // mutation this.$store.commit('moduleB/SET_B_NAME', { name: 'QQ' }); ...mapMutations({ setBname: 'moduleB/SET_B_NAME' }), // action this.$store.dispatch('moduleB/ASYNC_SET_NAME', { name: "JJ" }); ...mapActions({ aSetAge: 'moduleB/ASYNC_SET_NAME', }),
每次都要写模块名,这样写下来很烦,所以这些辅助函数给我们提供了一个参数位来绑定命名空间。
// moduleB 模块内的 bName ...mapState('moduleB', { name: state => state.bName }) // 同理 mapAction mapMutation 也可以这个样子 ...mapAction('moduleB',[ '/ASYNC_SET_NAME' ])
除了这个之外,如果你当前组件用的 vuex 状态都是一个模块的话,我们可以使用 createNamespacedHelpers 创建基于某个命名空间辅助函数,如下:
import { createNamespacedHelpers } from 'vuex' const { mapState, mapActions } = createNamespacedHelpers('moduleB') // moduleName
这样创建之后,我们就可以用之前的写法来访问到模块的状态。
...mapState({ bName: state => state.bName, }),
3.2.2 在带命名空间的模块内访问全局内容
如果你希望使用全局 state 和 getter,rootState 和 rootGetter 会作为第三和第四参数传入 getter,也会通过 context 对象的属性传入 action。
若需要在全局命名空间内分发 action 或提交 mutation,将 { root: true } 作为第三参数传给 dispatch 或 commit 即可。具体看下面代码: