Vuex | 具体实现篇 | 基础实现

621 阅读2分钟

第一阶段:实现全局数据管理

vuex入口文件暴露install方法和Store类,Vue.use默认会执行install方法并传递Vue的构造
let Vue;
// 默认会被传递Vue的构造
let install = (_vue) => {
    console.log(_vue);
    Vue = _vue;
}
class Store {
    
}

export default  {
    install,Store
}

install中使用mixin执行beforeCreate钩子,这样就可以在每个组件中执行一段逻辑

let install = (_vue) => {
    console.log(_vue);
    Vue = _vue;
    // mixin混入 实现每个组件上挂载$store属性
    Vue.mixin({
        beforeCreate(){
           
        }
    })
}

逻辑如下:判断是否为根组件,即$options(用户传递vue的对象)上有无store

  • 是,则挂载$store属性为store
  • 否,挂载store为父级的store为父级的store属性
 // mixin混入 实现每个组件上挂载$store属性
    Vue.mixin({
        beforeCreate(){
            // 判斷是否为根组件
            if(this.$options && this.$options.store) {
                this.$store = this.$options.store
            }else {
                this.$store = this.$parent && this.$parent.$store
            }
        }
    })

第二阶段:实现数据劫持,vuex数据改变也会触发视图更新

new Vue然后将state挂载到data

class Store {
    constructor(options){
        // this.s = options.state;
        let state = options.state;
        this._vm = new Vue({
            data: {
                state: state
            }
        })


    }
    get state(){
        return this._vm.state
    }
}

第三阶段:实现getters

初始化时为每个getter定义defineProerty,取值时执行其对应函数,将函数返回值返回

## vuex.js
if(options.getters){
            let getters = options.getters;
            forEach(getters, (getterName, getterFn) => {
                Object.defineProperty(this.getters,getterName,{
                    get:()=>{
                        return getterFn(state);
                    }
                })
            })
        }

借用vue中的computed,将getter定义在vm上;

## vuex.js

const computed = {}
this._vm = new Vue({
    data: {
        state: state
    },
    ### computed挂载到vue上
    computed
})

if(options.getters){
            let getters = options.getters;
            forEach(getters, (getterName, getterFn) => {
				### 将getter挂载到computed上
                computed[getterName] = () => {
                    return getterFn(this.state);
                }

                Object.defineProperty(this.getters,getterName,{
                    get:()=>{
                        ### 取值时直接从vm实例上获取 这样才能实现依赖收集
                        return this._vm[getterName]
                    }
                })
            })
}
function forEach(obj,callback){
    Object.keys(obj).forEach(item=>callback(item,obj[item]));
}

第四阶段:实现更改数据流程

store类上定义commit、dispatch方法,分别执行store上的mutation、action

## vuex.js

constructor(){
    if (options.mutations) {
            let mutations = options.mutations;
            forEach(mutations, (mutationName, mutationFn) => {
                this.mutations[mutationName] =()=> mutationFn.call(this,state);
            })
        }
        if (options.actions) {
            let actions = options.actions;
            forEach(actions, (actionName, actionFn) => {
                this.actions[actionName] =()=> actionFn.call(this,this);
            })
        }
}
 commit(type){
        console.log(this);
        // console.log(this.mutations[type]);
        this.mutations[type]()
    }
    dispatch(type){
        this.actions[type]()
    }

考虑原型链,在构造中再次定义commit、dispatch方法,内部以call执行原型上的commit/dispatch绑定this

 let {commit,dispatch} = this;
        this.commit = (type) => {
            commit.call(this,type)
        }
        this.dispatch = (type) => {
            dispatch.call(this,type)
        }

至此,我们完成了初版的vuex

完整代码如下

let Vue;
// 默认会被传递Vue的构造
let install = (_vue) => {
    console.log(_vue);
    Vue = _vue;
    // mixin混入 实现每个组件上挂载$store属性
    Vue.mixin({
        beforeCreate(){
            // 判斷是否为根组件
            if(this.$options && this.$options.store) {
                this.$store = this.$options.store
            }else {
                this.$store = this.$parent && this.$parent.$store
            }
        }
    })
}

class Store {
    constructor(options){
        // this.s = options.state;
        let state = options.state;
        this.getters = {};
        this.mutations = {};
        this.actions = {};

        const computed = {}

        this._vm = new Vue({
            data: {
                state: state
            },
            computed
        })


        if(options.getters){
            let getters = options.getters;
            forEach(getters, (getterName, getterFn) => {

                computed[getterName] = () => {
                    return getterFn(this.state);
                }

                Object.defineProperty(this.getters,getterName,{
                    get:()=>{
                        return getterFn(state);
                    }
                })
            })
        }
        if (options.mutations) {
            let mutations = options.mutations;
            forEach(mutations, (mutationName, mutationFn) => {
                this.mutations[mutationName] =()=> mutationFn.call(this,state);
            })
        }
        if (options.actions) {
            let actions = options.actions;
            forEach(actions, (actionName, actionFn) => {
                this.actions[actionName] =()=> actionFn.call(this,this);
            })
        }
        let {commit,dispatch} = this;
        this.commit = (type) => {
            commit.call(this,type)
        }
        this.dispatch = (type) => {
            dispatch.call(this,type)
        }


    }
    get state(){
        return this._vm.state
    }
    commit(type){
        console.log(this);
        // console.log(this.mutations[type]);
        this.mutations[type]()
    }
    dispatch(type){
        this.actions[type]()
    }
}

function forEach(obj,callback){
    Object.keys(obj).forEach(item=>callback(item,obj[item]));
}

export default  {
    install,Store
}