Vuex | 思路篇 | 第二版模块化

940 阅读3分钟

前言

  • 文章结构采用【指出阶段目标,然后以需解决问题为入口,以解决思路为手段】达到本文目标,若使诸君稍有启发,不枉此文心力^-^
  • 此文是vuex系列第二版,建议从第一版开始食用Vuex | 思路篇 | 初版,文首均有完成功能目录,可看是否需要

完成功能

  • vuex数据模块化

待完成功能

  • 辅助函数map系列(mapState、mapGetters、mapMutations、mapActions)
  • vuex插件机制

第一阶段:处理模块化--规格化

先看使用
 modules: {
    a: {
      state: {
        count:201
      },
      modules: {
        b: {
          state: {
            count:300
          },
          mutations: {
            add(state){
             console.log("b的add");
            }
          }
        }
      }
    }
  }
核心问题
  • 多层嵌套module
解决思路
  • 规格化+递归:其实我们可以养成一个思维惯性,这类需要深度递归的问题都可以采用这个方式,比如vue-router亦是如此
    • 先规定一个数据结构,vuex中定义是一个对象newModule,属性如下
      • _raw:object类型,当前子模块配置信息对象
      • _children:object,其下子模块信息,key为模块名(即a),val为子模块对应的newModule
      • state:obj,子模块的state对象
    • 深度递归,首先,我们需要将模块挂载到对应父节点上,所以需要一个路径,vuex中采用数组;其次需要module对象;所以我们的递归入口函数
      • 需输入:path--父模块路径数组(如对于b模块而言,为[a]);module--用户定义对象
      • 需执行:
        • 组装newModule对象
        • 判断是否为最外层,是则挂载在root属性上;不是挂载在path中对应的父模块的_children上
        • 判断有无modules属性,有则遍历递归(因为可能有多个子模块)

第二阶段:处理模块化的actions、mutations

先看使用

注意我们在根上定义了一个mutation为add,同时在a模块下的b模块也定义了一个同名的mutation

# store.js
export default new Vuex.Store({
  state: {
    count: 100
  },
  mutations: {
    add(state){
      state.a.b.count += 1;
      console.log("root的add");
    }
  },

  actions: {
    add({commit}){
      commit('add')
      // console.log(commit);
      // state.count += 1;
    }
  },
  modules: {
    a: {
      state: {
        count:201
      },
      modules: {
        b: {
          state: {
            count:300
          },
          mutations: {
            add(state){
             console.log("b的add");
            }
          }
        }
      }
    }
  }
})

此时页面点击按钮以action触发commit('add'),会发现很神奇的事情

# app.vue
 <button @click="clickMe">+1</button>
 
  methods: {
    clickMe(){
      this.$store.dispatch('add')
    }
  }

页面打印了两个log,也就是意味着都被触发了

也就是说明,对于mutations而言,后代模块同样会被收集,在触发时批量触发;actions同理

核心问题
  • 如何实现
解决思路

一次触发,多次执行;一对多优先考虑订阅发布;

  • 改写第一版逻辑,订阅时采用数组存储回调,先将后代模块的同名mutation/action收集到一个数组中
  • 触发时即遍历执行

第三阶段:处理模块化的state

先看使用
# store.js
const moduleA = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}

const moduleC = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  modules: {
      b: moduleB
  }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    c: moduleC
  }
})

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

核心问题
  • 实现挂载效果
  • 注意实现子模块数据的改变要驱动视图的改变,即vue托管数据
解决思路
  • installModule过程中,如果path存在(即有子模块),则将数据挂载在父模块上,key为子模块名,val为state
  • 挂载时不要直接挂载,对象属性的增删不会为vue所监听,可以使用Vue.set的方式进行设置(原因可见拙作(vue1.0版)数据观测 依赖收集))

具体实现链接

Vuex | 具体实现篇 | 模块化实现