前言 上回咱们说了一下 vuex 的简单使用,最后面的时候有说了,因为使用单一状态树,应用的全部状态会集中到一个比较大的对象。当应用变得很是复杂时,store 对象就有可能变得至关臃肿。html
为了解决以上问题,Vuex 容许咱们将 store 分割成模块(module)。每一个模块拥有本身的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行一样方式的分割,今天咱们也来简单了解一下他的使用,深刻学习可能仍是要去看官方文档vue
文件结构的话,模块化的使用要多一个 modules 的文件夹,里面放着细分模块的 js 文件/模块名文件夹。git
这里官方的标准是一个模块一个 js 文件,可是要是模块太复杂的话,也能够把里面的代码拆分出来。github
// store 文件夹 │ actions.js │ getters.js │ index.js │ mutations.js │ state.js │ └─modules │ moduleB.js │ └─moduleA index.js mutation.js state.js
而后在建立 store 的 js 文件中引入这些模块,直接vuex
import moduleA from './modules/moduleA/index' import moduleB from './modules/moduleB'; export default new Vuex.Store({ state, getters, mutations, actions, modules: { moduleA, moduleB, } });
模块内部的 getter,mutation 和 action,他们方法接收的参数会和根状态的不同,咱们一个一个来async
getter 的话,他会有三个参数,第一个是模块内的 state,第二个是 模块内的 getters,第三个是根节点状态 rootState,ide
const getters = { bFullName: (state, getters, rootState) => `full${state.bName}` }
mutation 里面的回调函数传入的第一个参数也是 模块内的 state,其余和根状态定义的时候同样模块化
const mutations = { // 这里的 `state` 对象是模块的局部状态 SET_B_NAME(state, payload) { debugger state.bName = payload.name; } }
最后的 action 的话,他传入仍是只有 context 对象,而后咧,这个对象里面的 state 属性指模块内的状态,rootState 指根状态,以下函数
const actions = { ASYNC_SET_NAME({ state, commit, rootState }, payload) { setTimeout(() => { state.bName = 'asyncName' }, 4000) } }
这个的话要在原来状态名前面加一个模块名才能放到到模块内的对象。具体以下学习
// 原先的基础上加个模块名 this.$store.state.moduleB.bName; // 辅助函数也同样,name 前面加个模块名 Deno ...mapState({ name: state => state.moduleB.bName, })
getter,mutation,action 他们默认都是注册在全局命名空间的,因此咱们默认是能够和使用根状态同样去使用他们,可是这样不可避免会出现命名冲突的问题,因此使模块有更高的封装性与复用性,咱们能够经过添加 `
namespaced: true` 使其成为带命名空间的模块。当模块被注册后,它的全部 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。
// moduleB 模块导出的时候加个 namespaced: true, export default { namespaced: true, state, getters, mutations, actions, }
由于有了命名空间这么一层封装,因此咱们在用辅助函数的时候都要多加那么一层模块名,具体看下面代码。
// 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, }),
若是你但愿使用全局 state 和 getter,rootState 和 rootGetter 会做为第三和第四参数传入 getter,也会经过 context 对象的属性传入 action。
若须要在全局命名空间内分发 action 或提交 mutation,将 { root: true } 做为第三参数传给 dispatch 或 commit 便可。具体看下面代码:
modules: { foo: { namespaced: true, getters: { // 在这个模块的 getter 中,`getters` 被局部化了 // 你可使用 getter 的第四个参数来调用 `rootGetters` someGetter (state, getters, rootState, rootGetters) { getters.someOtherGetter // -> 'foo/someOtherGetter 模块内的 getter' rootGetters.someOtherGetter // -> 'someOtherGetter 全局的getter' }, someOtherGetter: state => { ... } }, actions: { // 在这个模块中, dispatch 和 commit 也被局部化了 // 他们能够接受 `root` 属性以访问根 dispatch 或 commit someAction ({ dispatch, commit, getters, rootGetters }) { getters.someGetter // -> 'foo/someGetter' rootGetters.someGetter // -> 'someGetter' dispatch('someOtherAction') // -> 'foo/someOtherAction' 模块内的 action dispatch('someOtherAction', null, { root: true }) // ->'someOtherAction' 全局的 action commit('someMutation') // -> 'foo/someMutation' 模块内的 action commit('someMutation', null, { root: true }) // -> 'someMutation' 全局 mutation }, someOtherAction (ctx, payload) { ... } } } }
这个感受和维护模块的封装性有点冲突,可是既然做者提出来了,那就学吧,当咱们想要咱们模块内的某个 action 提高为全局 action 的时候,在他声明的时候,添加 root: true
,并将 action 的定义放到 hanler 函数中,具体以下:
const actions = { // 模块内 action [ASET_AGE]({ commit }, payload) { setTimeout(() => { commit('SET_B_NAME', payload.name); }, 2000) }, // 提高到全局的 action globalAction: { root: true, handler({ commit }, payload) { debugger setTimeout(() => { commit('SET_B_NAME', payload.name); }, 2000) } } }
关于模块使用 Vuex 的介绍就说到这里了,这两篇笔记的项目源码我发到了 GitHub 上面,你们能够去看一下,要是项目中有啥不明白的或者我说的有问题的,欢迎你们留言指正。