简版Vuex实现

124 阅读1分钟

Vuex

Vuex 集中式存储管理应⽤的所有组件的状态,并以相应的规则保证状态以可预测的⽅式发⽣变化。

核心概念

  • state 状态、数据
  • mutations 更改状态的函数
  • actions 异步操作
  • store 包含以上概念的容器

状态 - state

state保存应⽤状态

export default new Vuex.Store({
    state: { counter:0 },
})

状态变更 - mutations

mutations⽤于修改状态

export default new Vuex.Store({
    mutations: {
        add(state) {
            state.counter++
        }
    }
})

派⽣状态 - getters

从state派⽣出新状态,类似计算属性

export default new Vuex.Store({
    getters: {
        doubleCounter(state) { // 计算剩余数量
            return state.counter * 2;
        }
    }
})

动作 - actions

添加业务逻辑,类似于controller

export default new Vuex.Store({
    actions: {
        add({ commit }) {
            setTimeout(() => {
                commit('add')
            }, 1000)
        }
    }
})

实现一个简版的vuex

任务分析

  • 实现插件
    • 实现Store类
      • 维持一个响应式状态state
      • 实现commit()
      • 实现dispatch()
      • getters
    • 挂载#store

初始化:Store声明、install实现

let Vue;

class Store {
    constructor (options = {}) {
        this._vm = new Vue ({
            data: {
                $$state: options.state
            }
        })
    }

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

    set state () {
        console.error('please use replaceState to reset state')
    }
}

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

export default { Store, install }

实现commit:根据用户传入type获取并执行对应mutation

class Store {
    constructor (options = {}) {
        // 保存用户配置的mutations选项
        this._mutations = options.mutations || {}
    }

    commit (type, payload) {
        // 获取type对应的mutation
        const entry = this._mutations[type]
        if (!entry) {
            console.error(`unknown mutation type: ${type}`)
            return
        }
        // 指定上下文为Store实例
        // 传递state给mutation
        entry(this.state, payload)
    }
}

实现actions:根据用户传入type获取并执行对应action

class Store {
    constructor (options = {}) {
        // 保存用户编写的actions选项
        this._actions = options.actions || {}
        // 绑定commit上下文否则action中调用commit时可能出问题!!
        // 同时也把action绑了,因为action可以互调
        const store = this
        const { commit, action } = store
        this.commit = function boundCommit(type, payload) {
            commit.call(store, type, payload)
        }
        this.action = function boundAction(type, payload) {
            return action.call(store, type, payload)
        }
    }

    dispatch (type, payload) {
        // 获取用户编写的type对应的action
        const entry = this._actions[type]
        if (!entry) {
            console.error(`unknown action type:${type}`)
            return
        }
        // 异步结果处理常常需要返回Promise
        return entry(this, payload)
    }
}

实现getters:

class Store {
    constructor (options = {}) {
        // 保存用户配置的getters选项
        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: () => store._vm[key]
            })
        })
        this._vm = new Vue ({
            data: {
                $$state: options.state
            },
            computed: {
                
            }
        })
    }
}

简版vuex全部代码

let Vue;

class Store {
    constructor(options) {
    	this._mutations = options.mutations
        this._actions = options.actions

        this.commit = this.commit.bind(this)
        this.dispatch = this.dispatch.bind(this)

        // 保存用户配置的getters选项
        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: () => store._vm[key]
            })
        })
        
        // data响应式处理
        // this.$store.state.xx
        this._vm = new Vue({
            data: {
                $$state: options.state
            },
            computed
        })
    }

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

    set state(v) {
        console.error('please use replaceState to reset state');
    }

    commit(type, payload) {
        const entry = this._mutations[type]
        if (!entry) {
            console.error('unkown mutation type');
        }

        entry(this.state, payload)
    }

    dispatch(type, payload) {
        const entry = this._actions[type]
        if (!entry) {
            console.error('unkown action type');
        }

        entry(this, payload)
    }
  
}

// Vue.use
// install.apply(this, [this,...])
function install(_Vue) {
    Vue = _Vue

    Vue.mixin({
        beforeCreate() {
            if (this.$options.store) {
                Vue.prototype.$store = this.$options.store
            }
        }
    })
}

export default { Store, install };