Skip to content
On this page

100 行写一个 vuex

既然想 100 行拿下 vuex,思考一下 vuex 核心之核心的逻辑是什么?

Vuex 作为 Vue 插件,所以需要 install

全部代码实现:

js

let Vue;
class ModuleCollection { 
    constructor(options) {
        this.register([], options);
    }
    register(path, rawModule) {
        let newModule = {
            _raw: rawModule,
            _children: {}, 
            state: rawModule.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 (rawModule.modules) { // 有子模块
            forEach(rawModule.modules, (childName, module) => {
                // [a,b];
                // [a,d]
                this.register(path.concat(childName), module)
            });
        }
    }
}
// vuex 中间件  可以封装自己的逻辑  subscirbe registerModule unregisterModule
function installModule(store, rootState, path, rootModule) {
    // rootState.a = {count:200}
    // rootState.a.b = {count:3000}
    if (path.length > 0) { // [a,b]
        // 第二次 获取到的就是a对应的对象
        let parent = path.slice(0, -1).reduce((root, current) => {
            return root[current];
        }, rootState)
        // {count:1000,a:{}}
        Vue.set(parent, path[path.length - 1], rootModule.state);
    }
    if (rootModule._raw.getters) {
        forEach(rootModule._raw.getters, (getterName, getterFn) => {
            Object.defineProperty(store.getters, getterName, {
                get: () => {
                    return getterFn(rootModule.state);
                }
            });
        });
    }
    if (rootModule._raw.actions) {
        forEach(rootModule._raw.actions, (actionName, actionFn) => {
            let entry = store.actions[actionName] || (store.actions[actionName] = []);
            entry.push(() => {
                actionFn.call(store, store);
            })
        });
    }
    if (rootModule._raw.mutations) {
        forEach(rootModule._raw.mutations, (mutationName, mutationFn) => {
            let entry = store.mutations[mutationName] || (store.mutations[mutationName] = []);
            entry.push(() => {
                mutationFn.call(store, rootModule.state);
            })
        });
    }
    forEach(rootModule._children, (childName, module) => {
        installModule(store, rootState, path.concat(childName), module);
    })
}
class Store { 
    constructor(options) {
        let state = options.state; 
        this.getters = {};
        this.mutations = {};
        this.actions = {}
        this._vm = new Vue({
            data: {
                state
            }
        });

        this.modules = new ModuleCollection(options);

        installModule(this, state, [], this.modules.root); 
        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) {  
        this.mutations[type].forEach(fn => fn());
    }
    dispatch(type) {
        this.actions[type].forEach(fn => fn());
    }
}

function forEach(obj, callback) {
    Object.keys(obj).forEach(item => callback(item, obj[item]));
}
let 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 {
    Store,
    install
}