vuex的实现原理

426 阅读2分钟

Vue.use()方法

首先使用vue的插件,例如vue-router/vuex都要在引入后执行Vue.use(Xxx)如果传入的Xxx是个函数,就会直接调用这个函数并传入Vue这个类,如果是对象就直接调用里面的install方法并传入Vue这个类

代码

使用的时候引入Vuex然后Vue.use(Vuex),但是还有之后的new Vue.store({})就说明引入的Vuex是个对象,有个install方法,有个Store类,在install方法中为每一个组件添加一个$store属性指向Store实例,利用全局混入,为每个组件设计beforeCreate钩子函数,函数中的this指向当前组件

    let Vue=null
    function install(_Vue){
        Vue=_Vue
        //为每个组件添加$store
        Vue.minix({
            beforeCreate(){
                if(this.$options.store){
                    this.$store=this.$options.store
                }else if(this.$parent){
                    this.$store=this.$parent.$store
                }
            }
        })
    }
    class Store{
        constructor(options){
        //new Vue.Store({})传入的options中可能没有mutations/actions/getters
        //就把相应的mutations/actions/getters设为{}
            let {state,mutations,actions,getters}=options
            let vm=new Vue({
                data:{
                    state
                }
            })
            this.state=vm.state
            this.mutations=mutations||{}
            this.actions=actions||{} 
            //虽然在new Store传入的getters对象中是函数,但是它相当于computed
            //使用时this.$store.getters.xxx,得到xxx方法的结果,所以在store实例上添加的getters是一个对象,属性名是传入的函数名,属性值是函数执行之后的值,因为他会随着state值得变化而改变值所以就要把getters对象中得属性通过Object.defineProperty(this.getters,xxx,{get=()=>{}})当state中数据改变就要重新渲染页面重新计算getters中得值
            this.getters={}
            let newGet=getters||{}
            Object.keys(newGet).forEach(item=>{
            //为this.getters添加属性
                Object.defineProperty(this.getters,item,{
                    get=()=>{
                        return newGet[item].call(this,this.state)
                    }
                })
            }
            
        }
        commit(type,only1){
            this.mutations[type]&&this.mutations[type].call(this,this.state,only1)
        }
        dispatch(type,only1){
             this.actions[type]&&this.actions[type].call(this,this,only1)
        }
       
        
    }
    
    let Vuex={
        install,
        Store
    }
    export default Vuex
    
    
   //正常使用vuex的内容要this.$store.state.yyy或this.$store.commit("type",parmas)
  //这样是很麻烦的,于是就有了从文件中导入mapSate方法,传入一个数组或对象,把用到的state,放到数组中传给mapState方法
    export function mapState(ary){
        let obj={}
        if(Array.isArray(ary)){
            ary.forEach(item=>{
            obj[item]=function(){
                return this.$store.state[item]
            }
          })
        }else if(Object.prototype.toString.call(ary)==="[object Object]"){
            Object.keys(ary).forEach(item=>{
                obj[item]=function(){
                    return this.$store.state[ary[item]]
                }
            })
        }
        return obj
    }
    
    export function mapMutations(ary){
        let obj={}
        ary.forEach(item=>{
            obj[item]=function(params){
                this.$store.commit(item,params)
            }
        })
        return obj
    }
    
        export function mapActions(ary){
        let obj={}
        ary.forEach(item=>{
            obj[item]=function(params){
                this.$store.dispatch(item,params)
            }
        })
        return obj
    }
    
    export function mapGetters(ary){
        let obj={}
        ary.forEach(item=>{
            obj[item]=function(){
                return this.$store.getters[item]
            }
        })
        return obj
    }

总结

先从Vuex的使用说起,先引入Vuex,使用Vue.use(Vuex),然后再new Vuex.Store(options),options是vuex中的状态(state),mutaions/actions/getters,之后把创造的store实例暴露出去,在new Vue的根组件的options中挂载这个store实例,首先Vue.use(Vuex),是把引入的Vuex对象中的install方法执行并传入Vue,在这里利用全局混入为每个组件添加beforeCreate钩子函数,并在钩子函数中为当前组件添加$store,这个属性的属性值就是挂载的那个store实例,在Store类中,constructor中设定好state,通过new Vue()实现传入的state的相应式,把mutations和actions赋值好,向原型中添加commit和dispatch方法