手写vuex核心功能解析源码

vuex作用是为了实现公共状态共同管理,以便在项目当中实现共享状态。
首先导出 一个对象,对象里面有install方法和Store这个类。在使用vuex时要use,而使用use方法如果是对象的话需要一个install方法。Store类是用来创造一个store对象。

vuex导出的对象有install方法和Store类

export default{
    install,
    Store
}

1、install方法
install方法主要的作用是通过Vue.mixin混合,并通过beforeCreate钩子将store对象挂载到vue每个组件身上,也就是每个组件可以通过this.$store可以被访问到。

let Vue;
let install={
    install(_Vue){
        Vue=_Vue;
        Vue.mixin({
            beforeCreate(){     //将this.$store挂载到每个组件上
                if(this.$options&&this.$options.store){
                    //给根实例增加$store属性
                    this.$store=this.$options.store;
                }else{
                //给每个子组件添加$store属性,有可能单独创建了一个实例没有父亲,那就无法取到store属性
                    this.$store=this.$parent && this.$parent.$store
                }
            }
        })
    }
}

2、Store类传入对象包含state,mutations,getters,actions,module等一些属性。

  1. 通过将state属性放在Vue的data属性里面实现响应式数据变化。
import Vue from 'vue';
class Store{
    constructor(options){
    vuex定义了响应式数据变化,数据更新则视图更新。
        this.s=new Vue({    
            data(){
                return {state:options.state}
            }
        });
        this.getters={};    
        this.mutations={};
        this.actions={};
        this._subscribes=[];	订阅容器
        格式化options
        this._modules=new ModulesCollection(options);
        installModule(this,this.state,[],this._modules.root);
        subscribe(fn){
            this._subscribes.push(fn);
        }
        commit=(mutationsName,payload)=>{
            this.mutations[mutationsName].forEach(fn=>{
                fn(payload);
            })
        };
        dispatch =(actionsName,payload)=>{
           this.actions[actionsName].forEach(fn=>{
               fn(payload);
           })
            //源码里面有个变量,来控制是否通过mutaion来更新状态。
        }
        get state(){    //类的属性访问器
            return this.s.state
        }
    }
}
工具函数
//函数循环对象的key且拿到对象的每个key所对应的值。
function forEach(obj,cb){
    Object.keys(obj).forEach((key)=>{
        cb(key,obj[key]);
    })
}

将每个模块格式化成如下对象格式:
{
_rawModule:rootModule, //模块本身
_children:{}, //模块的子级
state:rootModule.state //模块自身的状态
}

class ModulesCollection{
    constructor(options){
        this.register([],options)
    };
    register(path,rootModule){
    	定义要格式化的对象格式
        let module ={
            _rawModule:rootModule,	模块本身
            _children:{},	模块子级
            state:rootModule.state	模块自身的状态
        };
        如果是根节点
        if(path.length===0){
            this.root=module;
        }else{
        处理根节点的_children的属性,用reduce实现递归,返回parent是一个对象
            let parent=path.slice(0,-1).reduce((root,current)=>{
                return root._children[current];
            },this.root)
            parent._children[path[path.length-1]]=module;
        }
        如果模块有module属性则递归实现格式化
        if(rootModule.module){
            forEach(rootModule.module,(moduleName,module)=>{
                this.register(path.concat(moduleName),module)
            })
        }
    }
}

将每个模块的actions,mutations,state,挂载到store上。

const installModule =(store,rootState,path,rootModule)=>{
    if(path.length>0){
        let parent=path.slice(0,-1).reduce((root,current)=>{
            return root[current];
        },rootState)
        Vue.set(parent,path[path.length-1],rootState.state);
    }
    获取根节点的getters
    let getters=rootModule._rawModule.getters;
    if(getters){
        forEach(getters,(gettersName,fn)=>{
            Object.defineProperty(getters,gettersName,{
                get(){
                    return fn(rootModule.state)
                }
            })
        })
    };
    获取根节点的mutations
    let mutations=rootModule._rawModule.mutations;
    if(mutations){
        forEach(mutations,(mutationsName,fn)=>{
            let mutations=store.mutations[mutationsName] || [];
            mutations.push((payload)=>{
                fn(rootModule.state,payload);
                // 发布 让所有的订阅依次执行。
                store.subscribe.forEach(fn=>{
                    fn({type:mutationsName,payload},rootState)
                })
            })
            store.mutations[mutationsName]=mutations;
        })
    };
    let actions=rootModule._rawModule.actions;
    if(actions){
        forEach(actions,(actionsName,fn)=>{
        	收集相同变量名的actions,等待发布的时候一起触发
            let actions=store.actions[actionsName] || [];   
            actions.push((payload)=>{
                fn(store,payload);
            });
            store.actions[actionsName]=actions;

        })
    };
    if(rootModule._children){
        forEach(rootModule._children,(moduleName,module)=>{
          installModule(store,rootState,path.concat(moduleName),module);
        })
    }
}
  数组基础
    var arr=[];
    arr.slice(start,end);start,end代表索引值。start为必输,-1代表最后一个元素。end可选(不包含end,不取索引值为end的值),如果为负数,表示从尾部开始算。生成新的数组,不会改变原数组。
    1.start和end都为正数  从索引值为start开始数起,到索引值为end(取end的前一位)因为不包含索引值为end的数。
    2.start和end都为负数,那么先计算(数组长度+start,数组长度+end),然后和正数一样数。也可以从尾部开始数起。
    arr.concat()。生成的新数组和原数组无引用关系。
    arr.reduce((pre,cur,index,arr)=>{},init)
    prev: 第一项的值或上一次叠加的结果值,表示上一次调用回调时的返回值,或者初始值 init;
    cur: 当前会参与叠加的项,若提供 init 值,则索引为0,否则索引为1。
    index: 当前值cur的索引
    arr: 数组本身
    init 表示初始值。

总结:
状态管理实现了数据的一个公共仓库,并且可实现数据的存取,以及组件与组件公共数据流的通信。vuex的核心功能的实现在基础上是摸透如何实现使用递归来实现module层级之间的关系。

你可能感兴趣的:(vue,vue.js,vuex)