Vuex简单实现

116 阅读1分钟

Vuex

Vuex实现了一个单向数据流,在全局拥有一个State存放数据,当组件要更改State中的数据时,必 须通过Mutation提交修改信息,Mutation同时提供了订阅者模式供外部插件调用获取State数据的 更新。 而当所有异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作需要走Action,但Action也是无法直接修改State的,还是需要通过Mutation来修改State的数据。最后,根据State 的变化,渲染到视图上。

856fda4fa393090d5213e2e5682d9c3.png

Vuex也是一个插件,需要提供install方法 供Vue使用,但是它的导出方式,跟router有一点区别

let Vue
class Store {
    constructor(options) {
        this.$options = options
        this._mutations = options.mutations
        this._actions = options.actions
        this._wrappedGetters=options.getters
        const computed={}
        this.getters={}
        //这里我们保存this指针
        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]
                }
            })
        })
        //这里通过VUE代理将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(v) {
        throw new Error('please use replaceState to reset state')
    }
    commit(type, payload) {
        const entry = this._mutations[type]
        if (!entry) {
            throw new Error('unknow mutations')
        }
        entry(this.state, payload)
    }
    dispatch(type, payload) {
        const entry = this._actions[type]
        if (!entry) {
            throw new Error('unknow actions')
        }
        // 这里因为actions内的方法参数具有store全部属性
        entry(this, payload)
    }
}

function install(_Vue) {
    Vue = _Vue
    Vue.mixin({
        beforeCreate() {
            if (this.$options.store) {
                Vue.prototype.$store = this.$options.store
            }
        },
    })
}

export default {
    Store,
    install
}