简单实现vuex

128 阅读3分钟

总体思路:

1.将用户传入的数据进行格式化操作(树结构)

2. 递归安装模块

具体:

1.使用vuex时,会使用Vue.use(vuex). 

use方法默认会调用vuex的install方法,并且会将Vue传入。

这样的好处是在vuex中不需要依赖vue的包。

install方法中,对vue生命周期做一次混合,将store放在每一个实例上。

function install(_vue) { 
   Vue = _vue;  Vue.mixin({    
      beforeCreate() {      
        if (this.$options.store) {        
          this.$store = this.$options.store;      
        } else { 
          this.$store = this.$parent && this.$parent.$store;      
        }    
      }  
    });
}

2. 用户调用new vuex.Store时,创建实例化Store(new vuex.Store)。实例化时会拿到用户传入的选项options--->class Store

获取到用户选项后,使用new Vue。目的是使数据实现响应式的变化,比如数据更新了视图可以刷新。--> 

    this.vm = new Vue({    
      data() {  
         return {          
           state: options.state 
         }      
       }    
    });   

核心思想是将用户传入的数据定义到actions、mutations、getters、state上。这样用户可以通过全局的状态去更新页面。

 this.getters = {};    
 this.mutations = {};    
 this.actions = {}; 

vuex可以传入modules,所以需要将用户传入的数据格式化。(vuex中的递归都是通过队列进行递归,这样可以清晰的知道父子层级关系)

this.modules = new ModulesConnect(options);//将用户传入的数据进行格式化操作

默认将用户传入的对象格式一个包含_raw(用户传入的原有对象)、_children(孩子)、state.将每个模块的state单独提出来,目的是为了后续安装方便。并在用户传入的模块中将格式化的结果记录下来,方便动态添加模块时安装。

默认作用于根模块,如果当前模块中有modules时,循环这个模块,继续注册它的子模块。

注册子模块时主要是依靠模块名将其拼在path中,继续注册,此时需要知道子模块的父级是谁,通过path.slice(0,-1),看其是否有父亲,如果没有父亲,将其定义在根模块的孩子中。如果有父亲,将其定义在父亲模块的_children中。这样就可以格式化一个树形结构中,

然后递归的安装模块,将刚格式化的数据。定义在Store上的actions、getters、mutations上。有儿子时会深度遍历。

function installModules(store, rootState, path, rawModules)

特殊的是state状态。子模块的状态需要使用vue.set方法。

if (path.length > 0) {    //子模块
     let parentState = path.slice(0, -1).reduce((root, current) => {      
       return rootState[current];    
     }, rootState);    
     Vue.set(parentState, path[path.length - 1], rawModules.state);  
  }  

动态添加模块时,判断传入的模块名是否是数组,如果不是数组则格式化成数组。添加到刚格式化的树结构中,然后进行注册。

function forEach(objs, callback) { 
   Object.keys(objs).forEach(name => {   
      return callback(name, objs[name]);
   })
}
class Store {  
  constructor(options) { 
    this.vm = new Vue({        
       data() {        
         return {          
           state: options.state 
         }      
       }    
    });    
   this.getters = {};    
   this.mutations = {};    
   this.actions = {};    
   this.modules = new ModulesConnect(options);//将用户传入的数据进行格式化操作    
   installModules(this, this.state, [], this.modules.root);//   递归安装模块  
  }  
  get state() {    
    return this.vm.state;  
  }  
  commit = (mutationName, payload) => {    
    this.mutations[mutationName].forEach(mutation=>{      
       mutation(payload);    
    }) 
  }  
  dispatch = (actionName, payload) => {  
    this.actions[actionName].forEach(action=>{      
       action(payload);    
    }); 
  }  
  registerModule(moudleName,module){ //动态添加组件    
     if(!Array.isArray(moudleName)){      
        moudleName = [moudleName];    
     }   
     this.modules.register(moudleName,module);     
     installModules(this,this.state,moudleName,module.rowModules)  
  }
}
function installModules(store, rootState, path, rawModules) {  
  let getters = rawModules._row.getters;  
  let actions = rawModules._row.actions;  
  let mutations = rawModules._row.mutations;  
  if (path.length > 0) {    
     let parentState = path.slice(0, -1).reduce((root, current) => {      
       return rootState[current];    
     }, rootState);    
     Vue.set(parentState, path[path.length - 1], rawModules.state);  
  }  
  if (getters) {    
     forEach(getters, (getterName, value) => {      
        Object.defineProperty(store.getters, getterName, {        
           get: () => {          
              value(store.state);        
           }      
        })    
      })  
   }  
   if (actions) {    
     forEach(actions, (actionName, value) => {      
        let arr = store.actions[actionName] || (store.actions[actionName] = []);      
        arr.push((payload) => {        
           value(store, payload);      
        })    
      });  
   }  
   if (mutations) {    
     forEach(mutations, (mutationName, value) => {      
       let arr = store.mutations[mutationName] || (store.mutations[mutationName] = []); 
       arr.push((payload) => {        
         value(store.state, payload);      
       })    
      });  
    }  
    if (rawModules.children) {    
      forEach(rawModules.children, (moudleName, modules) => {      
         installModules(store, rootState, path.concat(moudleName), modules);    
      });  
     }
    }
    class ModulesConnect {  
      constructor(options) {    
        this.register([], options);  
      }  
      register(path, rootModules) {   
        let rowModules = {      
           _row: rootModules,      
           children: {},      
           state: rootModules.state    
        }    
        rootModules.rowModules = rowModules;    
        if (!this.root) {     
          this.root = rowModules;    
        } else {      //[a] [b] [b c]      
          let parentMoudles = path.slice(0, -1).reduce((root, current) => {        
             return root.children[current];      
          }, this.root);      
          parentMoudles.children[path[path.length - 1]] = rowModules;   
        }    i
        if (rootModules.modules) {      
           forEach(rootModules.modules, (moudleName, modules) => {        
              this.register(path.concat(moudleName), modules);      
           });    
        }  
      }
    }