zoukankan      html  css  js  c++  java
  • Vuex简单实现

    Vuex简单实现

    1. 实现this.$store全局访问

    const install = (vue) => {
        Vue = vue;
        Vue.mixin({
            beforeCreate() {
                /* 获取根组件传递$store */
                if (this.$options && this.$options.store) {
                    this.$store = this.$options.store;
                } else {/* 从父组件传递 */
                    this.$store = this.$parent && this.$parent.$store;
                }
            }
        })
    }
    export default { install, Store }//导出
    

    Vue.use(vuex)的过程中会调用vuexinstall方法,同时把Vue作为形参传入,通过Vue.mixin给所有的组件添加生命周期事件,先给根组件设置this.$store = this.$options.store,然后让所有子组件继承

    2.Store类的实现

    class Store {
        constructor(options) {
            /* 初始化 */
            this.vm = new Vue({
                data: { state: options.state }
            })/* 使state里的数据响应式化 */
            this.getters = {};
            this.mutations = {};
            this.actions = {};
            /* 将modules转换格式 _raw _children state */
            this.modules = new ModuleCollection(options);
            console.log(this.modules);
            /* 安装模块 */
            installModules(this, this.state, [], this.modules.root);
        }
        get state() {/* 代理获取state */
            return this.vm.state;
        }
        commit = (key, ...payload) => {
            this.mutations[key].forEach(fn => fn(...payload));
        }
        dispatch = (key, ...payload) => {
            this.actions[key].forEach(fn => fn(...payload));
        }
        registerModule(moduleName, module) {
            /* 注册模块 */
            this.modules.register([moduleName], module);/* (path,rootModule) */
            /* 格式化后的模块 */
            let rawModule = this.modules.root._children[moduleName];
            installModules(this, this.state, [moduleName], rawModule);
        }
    }
    

    1.将state放到一个vue实例中,实现响应式,同时通过get state()实现代理
    2.初始化getters,mutations,actions
    3.创建一个ModuleColeection对象,将modules转化为标准格式

    class ModuleCollection {
        constructor(options) {
            this.register([], options)
        }
        /* path记录根到当前模块路径[b,c] */
        register(path, rootModule) {
            /* 创建当前模块的格式化对象 */
            let rawModule = {
                _raw: rootModule,
                _children: {},
                state: rootModule.state
            }
            /* 若还没有根,第一次进入,则给根模块赋值 */
            if (!this.root) {
                this.root = rawModule;
            } else {
                /* 找到当前模块父模块 [b,c] => this.root._children['b'] */
                let parent = path.slice(0, -1).reduce((data, item) => {
                    return data._children[item];
                }, this.root);
                /* 示例:this.root._children['b']._children['c']=rawModule */
                parent._children[path[path.length - 1]] = rawModule;
            }
            /* 遍历注册子模块 */
            if (rootModule.modules) {
                forEachValue(rootModule.modules, (moduleName, module) => {
                    this.register(path.concat(moduleName), module);
                })
            }
        }
    }
    

    ModuleColeection每次调用register方法都会创建一个对象rawModule,将每个模块的所有内容放到_raw中,将state数据放到state中,用_children来模块的直接子模块,第一次调用register时将options转化成的rawModule赋给this.root

    /* 创建当前模块的格式化对象 */
    let rawModule = {
        _raw: rootModule,
        _children: {},
        state: rootModule.state
    }
    

    利用封装的全局方法forEachValue取得子模块的名字和内容,递归调用子模块进行注册

    let forEachValue = (obj, callback) => {
        Object.keys(obj).forEach(key => {
            callback(key, obj[key]);
        })
    }
    /* 遍历注册子模块 */
    if (rootModule.modules) {
        forEachValue(rootModule.modules, (moduleName, module) => {
            this.register(path.concat(moduleName), module);
        })
    }
    

    path.concat的作用是记录路径,用于找到父模块,将自身放到父模块的_children对象中,形成图中格式,例如下面代码:在不是根模块的情况下,register传入的path=['b','c']时,就可以推断c模块属于第三层,通过前面的b找到父模块,再将自己放到父模块的_children对象。

    /* 找到当前模块父模块 [b,c] => this.root._children['b'] */
    let parent = path.slice(0, -1).reduce((data, item) => {
        return data._children[item];
    }, this.root);
    /* 示例:this.root._children['b']._children['c']=rawModule */
    parent._children[path[path.length - 1]] = rawModule;
    

    格式化后的形式
    4. installModules安装模块

    function installModules(store, rootState, path, rawModule) {
        /* 把所有数据放到state上 */
        if (path.length) {
            /* 获取父模块 示例:['b','c'] => rootState['b'] */
            let parent = path.slice(0, -1).reduce((data, item) => {
                return data[item];
            }, rootState);
            /* rootState['b']['c'] = rawModule.state */
            Vue.set(parent, path[path.length - 1], rawModule.state);
        }
        /* getters */
        let getters = rawModule._raw.getters;
        if (getters) {
            forEachValue(getters, (key, value) => {
                Object.defineProperty(store.getters, key, {
                    get: () => value(rawModule.state)
                })
            })
        }
        /* mutations */
        let mutations = rawModule._raw.mutations;
        if (mutations) {
            forEachValue(mutations, (mutationName, value) => {
                /* 收集所有模块的同名mutation */
                let arr = store.mutations[mutationName] || (store.mutations[mutationName] = []);
                arr.push((...payload) => { value(rawModule.state, ...payload) });
            })
        }
        /* actions */
        let actions = rawModule._raw.actions;
        if (actions) {
            forEachValue(actions, (actionName, value) => {
                let arr = store.actions[actionName] || (store.actions[actionName] = []);
                arr.push((...payload) => { value(store, ...payload) });
            })
        }
        /* 遍历子模块 */
        forEachValue(rawModule._children, (name, value) => {
            installModules(store, rootState, path.concat(name), value);
        })
    }
    

    实现在组件中类似{{$store.state.b.c.num}}的调用方式,原理就是将所有的数据根据嵌套关系放到state中,利用reduce寻找父模块,调用Vue.set添加响应式数据

    /* 把所有数据放到state上 */
    if (path.length) {
        /* 获取父模块 示例:['b','c'] => rootState['b'] */
        let parent = path.slice(0, -1).reduce((data, item) => {
            return data[item];
        }, rootState);
        /* rootState['b']['c'] = rawModule.state */
        Vue.set(parent, path[path.length - 1], rawModule.state);
    }
    

    对所有模块的getters进行劫持,直接使用$store.getters访问,限制就是模块之间命名不能重复

    /* getters */
    let getters = rawModule._raw.getters;
    if (getters) {
        forEachValue(getters, (key, value) => {
            Object.defineProperty(store.getters, key, {
                get: () => value(rawModule.state)//执行函数返回结果
            })
        })
    }
    

    将所有模块的mutations成员放到store.mutations对象上去,然后根据名称划分为多个订阅数组,commit调用时就可以直接触发所有模块执行同名的函数,actions区别在于传回的第一个参数时store,这样做的原因是实现actions到达事件后可以调用mutations成员执行操作

    /* ------------installMutations------------ */
    /* mutations */
    let mutations = rawModule._raw.mutations;
    if (mutations) {
        forEachValue(mutations, (mutationName, value) => {
            /* 收集所有模块的同名mutation */
            let arr = store.mutations[mutationName] || (store.mutations[mutationName] = []);
            arr.push((...payload) => { value(rawModule.state, ...payload) });
        })
    }
    /* actions */
    let actions = rawModule._raw.actions;
    if (actions) {
        forEachValue(actions, (actionName, value) => {
            let arr = store.actions[actionName] || (store.actions[actionName] = []);
            arr.push((...payload) => { value(store, ...payload) });
        })
    }
    /* -------------------Store------------------- */
    commit = (key, ...payload) => {
        this.mutations[key].forEach(fn => fn(...payload));
    }
    dispatch = (key, ...payload) => {
        this.actions[key].forEach(fn => fn(...payload));
    }
    

    递归安装子模块

    /* 遍历子模块 */
    forEachValue(rawModule._children, (name, value) => {
        installModules(store, rootState, path.concat(name), value);
    })
    

    5.registerModule方法拓展模块

    /* ------------Store方法------------ */
    registerModule(moduleName, module) {
        /* 注册模块 */
        this.modules.register([moduleName], module);/* (path,rootModule) */
        /* 格式化后的模块 */
        let rawModule = this.modules.root._children[moduleName];
        installModules(this, this.state, [moduleName], rawModule);
    }
    
    /* -----------外部调用方式----------- */
    /* 增加组件 */
    store.registerModule('d', {
        state: { num: 'd1' },
        modules: {
            e: { state: { num: 'e1' } }
        }
    })
    

    源码:https://gitee.com/aeipyuan/my_vuex

  • 相关阅读:
    问题解决:System.TypeLoadException: 未能从程序集“XXX”中加载类型“XXX
    域名格式验证
    .NET 中String 和StringBuilder 以及他们的区别
    mysql 重设管理员密码 或忘记管理员密码 (必杀)
    CMD 命令
    C# 给多线程传参的三种方式
    django 表单数据的验证实现原理
    django上传文件
    django signal
    Django如何处理语言偏好(根据此可以设置中文)
  • 原文地址:https://www.cnblogs.com/aeipyuan/p/12783491.html
Copyright © 2011-2022 走看看