# 实现一个vuex

# 实现一个mini-vuex

vuex就不做讲解和,核心就是一种递归方法的使用,这里就直接贴代码记录一下好了




 























































































































let Vue
const forEach = (obj, cb) => {
    Object.keys(obj).forEach(key => {
        cb(key, obj[key])
    })
}
class ModuleCollection {
    constructor(options) {
        this.register([], options)
    }
    register(path, rootModule) {
        let newModule = {
            _raw: rootModule,
            _children: {},
            state: rootModule.state
        }
        if (path.length === 0) { //第一次执行
            this.root = newModule
        } else { //后面递归执行
            let parent = path.slice(0, -1).reduce((root, current) => {
                return root._children[current]
            }, this.root)
            parent._children[path[path.length - 1]] = newModule //取数组最后一个
        }
        if (rootModule.modules) {
            forEach(rootModule.modules, (moduleName, module) => {
                this.register(path.concat(moduleName), module)
            })
        }
    }
}

// 递归树 将结果挂载到 getters mutations actions
const installModule = (store, state, path, rootModule) => {
    if (path.length > 0) { //如果是子模块 需要把子模块的状态放到父模块上
        const parent = path.slice(0, -1).reduce((state, current) => {
           return state[current]
        }, state)
        Vue.set(parent, path[path.length - 1], rootModule.state)
    }
    // 先处理根模块的getters属性
    const getters = rootModule._raw.getters
    if (getters) {
        forEach(getters, (getterName, fn) => {
            Object.defineProperty(store.getters, getterName, {
                get: () => {
                    return fn(rootModule.state)
                }
            })
        })
    }
    const mutations = rootModule._raw.mutations
    if (mutations) {
        forEach(mutations, (mutationName, fn) => {
            let arr = store.mutations[mutationName] || (store.mutations[mutationName] = [])
            arr.push((payload) => {
                fn(rootModule.state, payload)
            })
        })
    }
    const actions = rootModule._raw.actions
    if (actions) {
        forEach(actions, (actionName, fn) => {
            let arr = store.actions[actionName] || (store.actions[actionName] = [])
            arr.push((payload) => {
                fn(store, payload)
            })
        })
    }

    forEach(rootModule._children, (moduleName, module) => {
        installModule(store, state, path.concat(moduleName), module)
    })
   
}
class Store {
    constructor(options) {
        this._s = new Vue({
            data: {
                state: options.state // 把state变成可以监控的对象 state改变可以触发视图更新
            }
        })
        this.getters = {}
        this.mutations = {}
        this.actions = {}
        // 收集所有模块
        this.modules = new ModuleCollection(options)
        // 安装模块
        installModule(this, this.state, [], this.modules.root)
    }
    commit = (type, payload) => {
        this.mutations[type].forEach((fn) => {
            fn(payload)
        })
    }
    dispatch = (type, payload) => {
        this.actions[type].forEach((fn) => {
            fn(payload)
        })
    }
    get state() {
        return this._s.state
    }
}

const install = _Vue => {
    Vue = _Vue
    Vue.mixin({
        beforeCreate() {
            if (this.$options && this.$options.store) {
                this.$store = this.$options.store
            } else {
                this.$store = this.$parent && this.$parent.$store
            }
        }
    })
}

export default {
    install,
    Store
}