简单自定义vuex的设计思路

vuex集中式存储管理应用所有组件的状态,并以响应的规则保证状态以可预测的方式 发生变化。

步骤:

1.Store类,保存选项,_mutations,_actions,getters

2.响应式状态:new Vue方式设置响应式。

3.get state 和 set state commit dispatch

4.install方法 挂载store到vue的原型对象上,所有实例都可以读取到。

生成Vuex类

说到底,vuex也还是个插件,所以生成一个类,按照使用插件的形式将其export导出。

挂载到Vue实例上我们还是使用mixin的方式,原理和vue-router的自定义方式一致,这里就不多说了。

let Vue

class Store{
    constructor(options){
        //保存选项

        // state为响应式数据
        this.state = new Vue({
            data:options.state
        })
    }
}

function install(_Vue){
    Vue = _Vue
    //挂载$store给外面使用
    Vue.mixin({
        beforeCreate() {
            if(this.$options.store){
                //挂载到vue原型上,每个vue实例都可以访问到
                Vue.prototype.$store = this.$options.store
            }
        },
    })
}

export default {
    Store,install
}

但是在这里,我们使用new Vue的data属性来让state成为一个响应式的数据,有个弊端就是能够直接的修改data里的数据,那这样违反了vuex单向数据流的特点,那么我们要封装一下。

官网解释,有两个的$变量就不做代理,所以也就不会再_vm的根上访问到$$state这个属性,同时设置set get俩属性,get为只读,那么外面就无法修改state的值,只能通过commit或actions进行修改。_vm下的响应式对象会挂载在_data的对象上,_vm下的$data是一个原始对象,不具备响应式,所以使用_data。

如下图,$data不存在observer

简单自定义vuex的设计思路_第1张图片

class Store{
    constructor(options){
        //保存选项
        // state为响应式数据
        this._vm = new Vue({
            data:{
                $$state:options.state
            }
        })
    }
    //给用户暴露接口
    get state(){
        console.log(this._vm);
        return this._vm._data.$$state
    }
    set state(val){
        throw Error('replaceSate')
    }
}

commit和dispatch

响应式的数据发生变化就会引起render函数渲染数据 ,在commit中会接收到参数,我们在构造函数中存储传入的mutations和actions,然后在commit和dispach中匹配到详情的操作,然后执行。

但是在执行过程中有坑,this的指向问题:当前的调用是在外部,所以指向的是外部的store实例,是没有commit等参数,所以没有办法调用到。需要在constructor存储上下文,并且改变this指向到当前的实例,获取到对应的上下文即可。

class Store{
    constructor(options){
        //保存选项
        this._mutations  = options.mutations||{}
        this._actions  = options.actions||{}
        // state为响应式数据
        this._vm = new Vue({
            data:{
                $$state:options.state
            }
        })
        //上下文的绑定
        this.commit=this.commit.bind(this)
        this.dispatch=this.dispatch.bind(this)
    }
    //给用户暴露接口
    get state(){
        return this._vm._data.$$state
    }
    set state(val){
        throw Error('replaceSate')
    }
    //store.commit(type,payload)
    commit(type,payload){
        //获取mutitions
        const entry = this._mutations[type]
        if(!entry){
            console.error('unknown mutition type');
        }
        entry(this.state,payload)
    }
    dispatch(type,payload){
        const entry = this._actions[type]
        if(!entry){
            console.error('unknown actions type');
        }
        console.log(this);
        entry(this,payload)//注意这里的this指向问题,当前的调用是在外部,所以指向的是外部的store实例,是没有commit等参数,所以没有办法调用到。需要在constructor存储上下文,并且改变this指向。
    }
}

getters

getters我们可以借助computed属性,只可获取不可更改,获取到getters的key给到computed,并且在给一个函数,在其内部调用fn并且传入state,再将computed属性进行响应式处理。

this._wrappedGetters  = options.getters||{}
        //定义computed选项
        const computed={}
        this.getters={}
        const store= this
        Object.keys(this._wrappedGetters).forEach(key=>{
            //获取用户定义的getter
            const fn = store._wrappedGetters[key]
            // 转换为computed可以使用的无参数形式
            computed[key]=function(){
                return fn(store.state)
            }
            //为getters定义只读属性
            Object.defineProperty(store.getters,key,{
                get:()=>{
                    return store._vm[key]
                }
            })
        })

        // state为响应式数据
        this._vm = new Vue({
            data:{
                $$state:options.state
            },
            computed
        })

附完整代码:

let Vue

class Store{
    constructor(options){
        //保存选项
        this._mutations  = options.mutations||{}
        this._actions  = options.actions||{}
        this._wrappedGetters  = options.getters||{}
        //定义computed选项
        const computed={}
        this.getters={}
        const store= this
        Object.keys(this._wrappedGetters).forEach(key=>{
            //获取用户定义的getter
            const fn = store._wrappedGetters[key]
            // 转换为computed可以使用的无参数形式
            computed[key]=function(){
                return fn(store.state)
            }
            //为getters定义只读属性
            Object.defineProperty(store.getters,key,{
                get:()=>{
                    return store._vm[key]
                }
            })
        })

        // state为响应式数据
        this._vm = new Vue({
            data:{
                $$state:options.state
            },
            computed
        })
        //上下文的绑定
        this.commit=this.commit.bind(this)
        this.dispatch=this.dispatch.bind(this)
    }
    //给用户暴露接口
    get state(){
        return this._vm._data.$$state
    }
    set state(val){
        throw Error('replaceSate')
    }
    //store.commit(type,payload)
    commit(type,payload){
        //获取mutitions
        const entry = this._mutations[type]
        if(!entry){
            console.error('unknown mutition type');
        }
        entry(this.state,payload)
    }
    dispatch(type,payload){
        const entry = this._actions[type]
        if(!entry){
            console.error('unknown actions type');
        }
        entry(this,payload)//注意这里的this指向问题,当前的调用是在外部,所以指向的是外部的store实例,是没有commit等参数,所以没有办法调用到。需要在constructor存储上下文,并且改变this指向。
    }
}

function install(_Vue){
    Vue = _Vue

    //挂载$store给外面使用
    Vue.mixin({
        beforeCreate() {
            if(this.$options.store){
                //挂载到vue原型上,每个vue实例都可以访问到
                Vue.prototype.$store = this.$options.store
                
            }
        },
    })
}

export default {
    Store,install
}

你可能感兴趣的:(前端,javascript,开发语言)