vuex原理

  • state:单一状态树,类似于全局的一个容器
  • getter:相对于state的计算属性
  • mutations:定义同步方法
  • actions:定义异步逻辑,请求过后再去调用mutations里的方法

vuex原理图

ModuleCollection.js

export default class ModuleCollection{
  constructor(options) {
    this.register([], options);
  }

  register(path, rootModule) {
    let rawModule = {
      _raw: rootModule,
      _children: {},
      state: rootModule.state
    }
    if (!this.root) {
      this.root = rawModule;
    } else {
      let parentNode = path.slice(0,-1).reduce((root, current) => {
        return root._children[current];
      }, this.root);
      parentNode._children[path[path.length - 1]] = rawModule;
    }

    if (rootModule.modules) {
      Object.keys(rootModule.modules).forEach(moduleName => {
        this.register(path.concat(moduleName), rootModule.modules[moduleName]);
      });
    }
  }
}
export default new Vuex.Store({
  state: {
    msg: 10
  },
  getters: {
    add(state) {
      return state.msg + 20;
    }
  },
  mutations: {
    syncAddTen(state, payload) {
      console.log('最外层');
      state.msg += payload;
    }
  },
  actions: {
    asyncAddTen({ commit }, payload) {
      commit('syncAddTen', payload);
    }
  },
  modules: {
    a: {
      state: {
        msg_a: 10
      },
      mutations: {
        syncAddTen() {
          console.log('hello a');
        }
      }
    },
    b: {
      modules: {
        c: {
          state: {
            x: 12
          },
          mutations: {
            syncAddTen() {
              console.log('hello c');
            }
          }
        },
        d: {
          mutations: {
            syncAddTen() {
              console.log('hello d');
            }
          }
        }
      }
    },
    f: {
      mutations: {
        syncAddTen() {
          console.log('hello f');
        }
      }
    }
  }
})
  • 目的是根据以上的代码构建出以下一个对象
  • 核心算法就是使用path数组推进路径,比如,将b格式化模块挂载到a格式化模块下面,path=[…..,‘a’,‘b’],使用数组reduce方法推进到a模块那一层,然后将b的格式化模块挂载到a格式化模块的_children属性下

ModuleCollection类构建的目标对象

initModule.js

export default function installModule(Vue, store, rootState, path, rawModule) {
  if (path.length > 0) {
    let parentState = path.slice(0, -1).reduce((root, current) => {
      return rootState[current];
    }, rootState);
    
    
    Vue.set(parentState, path[path.length - 1], rawModule.state ? rawModule.state : {});
  }

  let getters = rawModule._raw.getters;
  if (getters) {
    Object.keys(getters).forEach(getterName => {
      Object.defineProperty(store.getters, getterName, {
        get() {
          return getters[getterName](rawModule.state);
        }
      });
    });
  }

  let mutations = rawModule._raw.mutations;
  if (mutations) {
    Object.keys(mutations).forEach(mutationName => {
      store.mutations[mutationName] = store.mutations[mutationName] ?
        store.mutations[mutationName] : [];
      store.mutations[mutationName].push(payload => {
        mutations[mutationName](rootState, payload);
      });
    });
  }

  let actions = rawModule._raw.actions;
  if (actions) {
    Object.keys(actions).forEach(actionName => {
      store.actions[actionName] = store.actions[actionName] ?
        store.actions[actionName] : [];
      store.actions[actionName].push(payload => {
        actions[actionName](store, payload);
      });
    });
  }

  if (rawModule._children) {
    Object.keys(rawModule._children).forEach(moduleName => {
      installModule(Vue, store, rootState, path.concat(moduleName), rawModule._children[moduleName]);
    });
  }
}
  • 这个函数的主要作用就是将子孙模块的state、getters、mutations、actions全部挂载到它们的根节点上
  • 算法思想:递归,对于state还是使用path拼接

index.js

import ModuleCollection from './MuduleCollection';
import installModule from './initModule';

let Vue;

class Store{
  constructor(options) {
    this.vm = new Vue({
      data: {
        state: options.state
      }
    });
    
    // this.getters = {};
    // this.mutations = {};
    // this.actions = {};

    this.modules = new ModuleCollection(options);

    installModule(Vue, this, this.state, [], this.modules.root);
    
  }

  commit = (mutationName, payload) => {
    this.mutations[mutationName].forEach(fn => {
      fn(payload);
    });
  }

  dispatch = (actionName, payload) => {
    this.actions[actionName].forEach(fn => {
      fn(payload);
    });
  }

  get state() {
    return this.vm.state;
  }

  // register(moduleName, module) {
  //   if (!Array.isArray(moduleName)) {
  //     moduleName = [moduleName];
  //   }

  //   this.modules.register(moduleName, module);

  //   installModule(Vue, this, this.state, [], this.modules.root);
  // }
}

const install = (_vue) => {
  Vue = _vue;
  /**
   * 每次new一个vue实例时,都会去设置它的$store属性
   */
  Vue.mixin({
    beforeCreate() {
      //根实例
      if (this.$options.store) {
        this.$store = this.$options.store;
      //子组件
      } else {
        this.$store = this.$parent && this.$parent.$store;
      }
    }
  });
}

export default {
  Store,
  install
}
  • 当外部使用Vue.use的时候,会默认去调用插件的install方法,以上的install方法里面使用传入的全局Vue向之后所有通过new运算符创建的vue实例,挂载上了$store
  • 全局混入api可以查看官方文档 全局混入