手撕Vuex---------Vuex实现原理分析__Vue.js
发布于 3 年前 作者 banyungong 1161 次浏览 来自 分享
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利

Vuex特点1:

  • 使用Vuex的时候需要用到Vue的use方法,所以VueX本质是一个插件,所以实现Vuex就是在实现一个全局共享数据的插件

特点2:

  • 在使用Vuex的时候我们会通过Vuex.Store创建一个仓库,所以还需要在Vuex中新增Store属性,这个属性的取值是一个类.

特点3:

  • 为了保证每个Vue实例中都能通过this.$ store拿到仓库,还需要给每个Vue实例都动态添加$store属性.

1.添加全局$store

//外界调用vue.use会执行install,并且会在执行时会把Vue实例和可选参数传递过来
const install = (Vue, options) => {
    // 给每一个Vue实例添加一个$store属性
    // vue中有一个名称叫做mixin的方法,这个方法会在创建每一个vue实例时执行
    // 所以可以通过mixin给每一个实例添加$store属性,mixin接收一个对象
    Vue.mixin({
        // 重写每一个vue实例的生命周期方法
        beforeCreate() {
            // Vue在创建实例的时候会先创建父组件,然后创建子组件
            // console.log(this.$options.name);
            // Root->App->Helloworld
            //如果是根组件,那么默认有store,所以只需要将store变成$store即可
            if (this.$options && this.$options.store) {
                this.$store = this.$options.store
            } else {
                // 如果不是根组件,默认没有store,将父亲的$store赋值给子代
                this.$store = this.$parent.$store
            }
        }
    })
}
// 在Vuex中新增Store属性,这个属性的取值是一个类
class Store {
    // 调用的时候会传递一个对象参数,也就是仓库
    constructor(options) {
        this.options = options
    }
}

//暴露
export default {
    install,
    Store
}

2.实现共享数据

//外界调用vue.use会执行install,并且会在执行时会把Vue实例和可选参数传递过来
const install = (Vue, options) => {
    // 给每一个Vue实例添加一个$store属性
    // vue中有一个名称叫做mixin的方法,这个方法会在创建每一个vue实例时执行
    // 所以可以通过mixin给每一个实例添加$store属性,mixin接收一个对象
    Vue.mixin({
        // 重写每一个vue实例的生命周期方法
        beforeCreate() {
            // Vue在创建实例的时候会先创建父组件,然后创建子组件
            // console.log(this.$options.name);
            // Root->App->Helloworld
            //如果是根组件,那么默认有store,所以只需要将store变成$store即可
            if (this.$options && this.$options.store) {
                this.$store = this.$options.store
            } else {
                // 如果不是根组件,默认没有store,将父亲的$store赋值给子代
                this.$store = this.$parent.$store
            }
        }
    })
}
// 在Vuex中新增Store属性,这个属性的取值是一个类
class Store {
    // 调用的时候会传递一个对象参数,也就是仓库
    constructor(options) {
        // this.options = options
        // =============================更新==========================
        // 将创建Store时需要共享的数据添加到Store上面
        //这样将来就可以通过this.$store拿到这个Store
        // 既然能拿到这个Store, 就可以通过.state拿到需要共享的属性
        this.state = options.state
    }
}

//暴露
export default {
    install,
    Store
}

效果 在这里插入图片描述

3.实现getters方法

//外界调用vue.use会执行install,并且会在执行时会把Vue实例和可选参数传递过来
const install = (Vue, options) => {
    // 给每一个Vue实例添加一个$store属性
    // vue中有一个名称叫做mixin的方法,这个方法会在创建每一个vue实例时执行
    // 所以可以通过mixin给每一个实例添加$store属性,mixin接收一个对象
    Vue.mixin({
        // 重写每一个vue实例的生命周期方法
        beforeCreate() {
            // Vue在创建实例的时候会先创建父组件,然后创建子组件
            // console.log(this.$options.name);
            // Root->App->Helloworld
            //如果是根组件,那么默认有store,所以只需要将store变成$store即可
            if (this.$options && this.$options.store) {
                this.$store = this.$options.store
            } else {
                // 如果不是根组件,默认没有store,将父亲的$store赋值给子代
                this.$store = this.$parent.$store
            }
        }
    })
}
// 在Vuex中新增Store属性,这个属性的取值是一个类
class Store {
    // 调用的时候会传递一个对象参数,也就是仓库
    constructor(options) {
        // this.options = options
        // 将创建Store时需要共享的数据添加到Store上面
        //这样将来就可以通过this.$store拿到这个Store
        // 既然能拿到这个Store, 就可以通过.state拿到需要共享的属性
        this.state = options.state
        // 将传递进来的getters放到Store上
        this.initGetters(options)
    }
    initGetters(options) {
        // 1.拿到传递进来的getters
        let getters = options.getters || {}
        // 2.在Store上新增一个getters的属性
        this.getters = {}
        //3.遍历传递进来的getters中的方法添加到当前Store的getters上
        for (let key in getters) {
            //    console.log(key);
            Object.defineProperty(this.getters, key, {
                get: () => {
                    return getters[key](this.state)
                }
            })
        }
    }
}

//暴露
export default {
    install,
    Store
}

效果 在这里插入图片描述

4.实现mutation方法

// =======================更新=============================
import Vue from 'vue'
//外界调用vue.use会执行install,并且会在执行时会把Vue实例和可选参数传递过来
const install = (Vue, options) => {
    // 给每一个Vue实例添加一个$store属性
    // vue中有一个名称叫做mixin的方法,这个方法会在创建每一个vue实例时执行
    // 所以可以通过mixin给每一个实例添加$store属性,mixin接收一个对象
    Vue.mixin({
        // 重写每一个vue实例的生命周期方法
        beforeCreate() {
            // Vue在创建实例的时候会先创建父组件,然后创建子组件
            // console.log(this.$options.name);
            // Root->App->Helloworld
            //如果是根组件,那么默认有store,所以只需要将store变成$store即可
            if (this.$options && this.$options.store) {
                this.$store = this.$options.store
            } else {
                // 如果不是根组件,默认没有store,将父亲的$store赋值给子代
                this.$store = this.$parent.$store
            }
        }
    })
}
// 在Vuex中新增Store属性,这个属性的取值是一个类
class Store {
    // 调用的时候会传递一个对象参数,也就是仓库
    constructor(options) {
        // this.options = options
        // 将创建Store时需要共享的数据添加到Store上面
        //这样将来就可以通过this.$store拿到这个Store
        // 既然能拿到这个Store, 就可以通过.state拿到需要共享的属性
        // =================================更新=============================
        // 在vue中有个util的工具类,通过defineReactive 方法,可以快速将某个数据变成双向绑定
        //这个方法接收三个参数:给哪个对象添加属性,给指定对象添加什么属性,要给属性添加什么值
        Vue.util.defineReactive (this,'state',options.state)
        // 将传递进来的getters放到Store上
        this.initGetters(options)
        // =================================更新=============================
        // 将传递进来的mutations放到Store上
        this.initMutions(options)
    }
    // =================================更新=============================
    commit(type,payload){//'addNum',10
        this.mutations[type](payload)
    }
    initMutions(options){
        // 1.拿到传递进来的mutation
        let mutations = options.mutations || {}
        // 2.在Store上新增一个mutation的属性
        this.mutations = {}
        //3.遍历传递进来的mutation中的方法添加到当前Store的mutation上
        for(let key in mutations){
            this.mutations[key]=(payload)=>{//10
                // mutations['addNum'](num,10)
                mutations[key](this.state,payload)
            }
        }
    }
    initGetters(options) {
        // 1.拿到传递进来的getters
        let getters = options.getters || {}
        // 2.在Store上新增一个getters的属性
        this.getters = {}
        //3.遍历传递进来的getters中的方法添加到当前Store的getters上
        for (let key in getters) {
            //    console.log(key);
            Object.defineProperty(this.getters, key, {
                get: () => {
                    return getters[key](this.state)
                }
            })
        }
    }
}

//暴露
export default {
    install,
    Store
}

效果 在这里插入图片描述

5.实现actions方法

import Vue from 'vue'
//外界调用vue.use会执行install,并且会在执行时会把Vue实例和可选参数传递过来
const install = (Vue, options) => {
    // 给每一个Vue实例添加一个$store属性
    // vue中有一个名称叫做mixin的方法,这个方法会在创建每一个vue实例时执行
    // 所以可以通过mixin给每一个实例添加$store属性,mixin接收一个对象
    Vue.mixin({
        // 重写每一个vue实例的生命周期方法
        beforeCreate() {
            // Vue在创建实例的时候会先创建父组件,然后创建子组件
            // console.log(this.$options.name);
            // Root->App->Helloworld
            //如果是根组件,那么默认有store,所以只需要将store变成$store即可
            if (this.$options && this.$options.store) {
                this.$store = this.$options.store
            } else {
                // 如果不是根组件,默认没有store,将父亲的$store赋值给子代
                this.$store = this.$parent.$store
            }
        }
    })
}
// 在Vuex中新增Store属性,这个属性的取值是一个类
class Store {
    // 调用的时候会传递一个对象参数,也就是仓库
    constructor(options) {
        // this.options = options
        // 将创建Store时需要共享的数据添加到Store上面
        //这样将来就可以通过this.$store拿到这个Store
        // 既然能拿到这个Store, 就可以通过.state拿到需要共享的属性
        // 在vue中有个util的工具类,通过defineReactive 方法,可以快速将某个数据变成双向绑定
        //这个方法接收三个参数:给哪个对象添加属性,给指定对象添加什么属性,要给属性添加什么值
        Vue.util.defineReactive (this,'state',options.state)
        // 将传递进来的getters放到Store上
        this.initGetters(options)
        // 将传递进来的mutations放到Store上
        this.initMutions(options)
        // =======================更新==================
         // 将传递进来的actions放到Store上
         this.initActions(options)
    }
    dispatch=(type,payload)=>{//asyncAddNum 10
        this.actions[type](payload)// this.actions['asyncAddNum'](10)
    }
    initActions(options){
         // 1.拿到传递进来的actions
         let actions = options.actions || {}
         // 2.在Store上新增一个actions的属性
         this.actions = {}
         //3.遍历传递进来的mutation中的方法添加到当前Store的actions上
         for(let key in actions){
             this.actions[key]=(payload)=>{
                 actions[key](this,payload)//actions['asyncAddNum'](this,10)
             }
         }
    }
    // ====================更新===============
    commit=(type,payload)=>{//'addNum',10
        this.mutations[type](payload)
    }
    initMutions(options){
        // 1.拿到传递进来的mutation
        let mutations = options.mutations || {}
        // 2.在Store上新增一个mutation的属性
        this.mutations = {}
        //3.遍历传递进来的mutation中的方法添加到当前Store的mutation上
        for(let key in mutations){
            this.mutations[key]=(payload)=>{//10
                // mutations['addNum'](num,10)
                mutations[key](this.state,payload)
            }
        }
    }
    initGetters(options) {
        // 1.拿到传递进来的getters
        let getters = options.getters || {}
        // 2.在Store上新增一个getters的属性
        this.getters = {}
        //3.遍历传递进来的getters中的方法添加到当前Store的getters上
        for (let key in getters) {
            //    console.log(key);
            Object.defineProperty(this.getters, key, {
                get: () => {
                    return getters[key](this.state)
                }
            })
        }
    }
}

//暴露
export default {
    install,
    Store
}

效果 在这里插入图片描述

6.模块化共享数据

1.以上我们将所有模块的数据都放到了state中共享,但这样会导致命名匮乏的问题,例如有三个模块:首页/个人中心/登录,都需要共享name,并且这三个name的取值还不一样,可能就需要想六个命名.
2.为了解决这一问题,vuex就推出了模块化数据,通过模块化共享数据,可以将不同模块共享的数据放到不同的state中 注意点:

  • getters中获取的共享的数据,不需要加上模块的名称,但不能同名
  • 其余的属性可以同名,但需要加上模块名称

提取模块信息

自定义vuex.js

import Vue from 'vue'
//外界调用vue.use会执行install,并且会在执行时会把Vue实例和可选参数传递过来
const install = (Vue, options) => {
    // 给每一个Vue实例添加一个$store属性
    // vue中有一个名称叫做mixin的方法,这个方法会在创建每一个vue实例时执行
    // 所以可以通过mixin给每一个实例添加$store属性,mixin接收一个对象
    Vue.mixin({
        // 重写每一个vue实例的生命周期方法
        beforeCreate() {
            // Vue在创建实例的时候会先创建父组件,然后创建子组件
            // console.log(this.$options.name);
            // Root->App->Helloworld
            //如果是根组件,那么默认有store,所以只需要将store变成$store即可
            if (this.$options && this.$options.store) {
                this.$store = this.$options.store
            } else {
                // 如果不是根组件,默认没有store,将父亲的$store赋值给子代
                this.$store = this.$parent.$store
            }
        }
    })
}
// ================================================更新===========================
// 定义模块信息的类
class ModuleCollection {
    constructor(rootModule) {
        // 独立的方法来格式化
        this.register([], rootModule)
    }
    register(arr, rootModule) {
        // console.log(arr);//["login", "account"]
        // console.log(rootModule.state);
        // console.log(rootModule);
        // 1.按照需要的格式创建模块
        let module = {
            _raw: rootModule,
            _state: rootModule.state,
            _children: {}
        }
        //2.保存模块信息
        // 判断是根模块还是子模块
        if (arr.length == 0) {
            this.root = module//保存根模块
        } else {
            //保存子模块
            // this.root._children[arr[arr.length-1]]=module
            // let testArr= ["login", "account"]
            // let res=testArr.splice(0,testArr.length-1)
        //     // console.log(res);
          let parent=  arr.splice(0, arr.length - 1).reduce((root, currentKey) => {
                return root._children[currentKey]
            }, this.root)
            parent._children[arr[arr.length-1]]=module
        } // 3.处理子模块,遍历跟模块中的modules
        for (let childrenModuleName in rootModule.modules) {
            let childrenModule = rootModule.modules[childrenModuleName]
            // console.log(childrenModule);
            // 开始递归,将子模块格式处理好后添加到children
            // concat() 方法用于连接两个或多个数组。
            this.register(arr.concat(childrenModuleName), childrenModule)
        }
    }
}
// 在Vuex中新增Store属性,这个属性的取值是一个类
class Store {
    // 调用的时候会传递一个对象参数,也就是仓库
    constructor(options) {
        // this.options = options
        // 将创建Store时需要共享的数据添加到Store上面
        //这样将来就可以通过this.$store拿到这个Store
        // 既然能拿到这个Store, 就可以通过.state拿到需要共享的属性
        // 在vue中有个util的工具类,通过defineReactive 方法,可以快速将某个数据变成双向绑定
        //这个方法接收三个参数:给哪个对象添加属性,给指定对象添加什么属性,要给属性添加什么值
        Vue.util.defineReactive(this, 'state', options.state)
        // 将传递进来的getters放到Store上
        this.initGetters(options)
        // 将传递进来的mutations放到Store上
        this.initMutions(options)
        // 将传递进来的actions放到Store上
        this.initActions(options)
        //  ==================更新====================================
        //提取模块信息
        this.modules = new ModuleCollection(options)
        // console.log(this.modules);
    }
    dispatch = (type, payload) => {//asyncAddNum 10
        this.actions[type](payload)// this.actions['asyncAddNum'](10)
    }
    initActions(options) {
        // 1.拿到传递进来的actions
        let actions = options.actions || {}
        // 2.在Store上新增一个actions的属性
        this.actions = {}
        //3.遍历传递进来的mutation中的方法添加到当前Store的actions上
        for (let key in actions) {
            this.actions[key] = (payload) => {
                actions[key](this, payload)//actions['asyncAddNum'](this,10)
            }
        }
    }
    commit = (type, payload) => {//'addNum',10
        this.mutations[type](payload)
    }
    initMutions(options) {
        // 1.拿到传递进来的mutation
        let mutations = options.mutations || {}
        // 2.在Store上新增一个mutation的属性
        this.mutations = {}
        //3.遍历传递进来的mutation中的方法添加到当前Store的mutation上
        for (let key in mutations) {
            this.mutations[key] = (payload) => {//10
                // mutations['addNum'](num,10)
                mutations[key](this.state, payload)
            }
        }
    }
    initGetters(options) {
        // 1.拿到传递进来的getters
        let getters = options.getters || {}
        // 2.在Store上新增一个getters的属性
        this.getters = {}
        //3.遍历传递进来的getters中的方法添加到当前Store的getters上
        for (let key in getters) {
            //    console.log(key);
            Object.defineProperty(this.getters, key, {
                get: () => {
                    return getters[key](this.state)
                }
            })
        }
    }
}

//暴露
export default {
    install,
    Store
}

index.js

import Vue from 'vue'
import Suex from './Suex'

// ===============================更新=========================
// =====================要注意顺序,否则报错==========================
Vue.use(Suex)
let login={
  //用于保存共享数据
  state: {
    name: '杀生丸',
    num: 0
  },
  mutations: {
  },
  //用于异步修改共享数据
  actions: {
  },
  //用于模块化共享数据
  modules: {
  },
  getters: {
  }
}
let account={
  //用于保存共享数据
  state: {
    name: '胖虎',
    num: 0
  },
  mutations: {
  },
  //用于异步修改共享数据
  actions: {
  },
  //用于模块化共享数据
  modules: {
    login
  },
  getters: {
  }
}

export default new Suex.Store({
  //用于保存共享数据
  state: {
    name: '山竹回家了',
    num: 0
  },
  mutations: {
    addNum(state, payload) {
      state.num += payload
    }
  },
  //用于异步修改共享数据
  actions: {
    asyncAddNum({commit},payload){
      setTimeout(()=>{
        commit('addNum',payload)
      },3000)
    }
  },
  //用于模块化共享数据
  modules: {
    account
  },
  getters: {
    // 相当于vuex的计算属性
    myName(state) {
      return state.name + '111'
    },
  }
})

7.安装模块数据

import Vue from 'vue'
//外界调用vue.use会执行install,并且会在执行时会把Vue实例和可选参数传递过来
const install = (Vue, options) => {
    // 给每一个Vue实例添加一个$store属性
    // vue中有一个名称叫做mixin的方法,这个方法会在创建每一个vue实例时执行
    // 所以可以通过mixin给每一个实例添加$store属性,mixin接收一个对象
    Vue.mixin({
        // 重写每一个vue实例的生命周期方法
        beforeCreate() {
            // Vue在创建实例的时候会先创建父组件,然后创建子组件
            // console.log(this.$options.name);
            // Root->App->Helloworld
            //如果是根组件,那么默认有store,所以只需要将store变成$store即可
            if (this.$options && this.$options.store) {
                this.$store = this.$options.store
            } else {
                // 如果不是根组件,默认没有store,将父亲的$store赋值给子代
                this.$store = this.$parent.$store
            }
        }
    })
}
// 定义模块信息的类
class ModuleCollection {
    constructor(rootModule) {
        // 独立的方法来格式化
        this.register([], rootModule)
    }
    register(arr, rootModule) {
        // console.log(arr);//["login", "account"]
        // console.log(rootModule.state);
        // console.log(rootModule);
        // 1.按照需要的格式创建模块
        let module = {
            _raw: rootModule,
            _state: rootModule.state,
            _children: {}
        }
        //2.保存模块信息
        // 判断是根模块还是子模块
        if (arr.length == 0) {
            this.root = module//保存根模块
        } else {
            //保存子模块
            // this.root._children[arr[arr.length-1]]=module
            // let testArr= ["login", "account"]
            // let res=testArr.splice(0,testArr.length-1)
            //     // console.log(res);
            let parent = arr.splice(0, arr.length - 1).reduce((root, currentKey) => {
                return root._children[currentKey]
            }, this.root)
            parent._children[arr[arr.length - 1]] = module
        } // 3.处理子模块,遍历跟模块中的modules
        for (let childrenModuleName in rootModule.modules) {
            let childrenModule = rootModule.modules[childrenModuleName]
            // console.log(childrenModule);
            // 开始递归,将子模块格式处理好后添加到children
            // concat() 方法用于连接两个或多个数组。
            this.register(arr.concat(childrenModuleName), childrenModule)
        }
    }
}
// 在Vuex中新增Store属性,这个属性的取值是一个类
class Store {
    // 调用的时候会传递一个对象参数,也就是仓库
    constructor(options) {
        // this.options = options
        // 将创建Store时需要共享的数据添加到Store上面
        //这样将来就可以通过this.$store拿到这个Store
        // 既然能拿到这个Store, 就可以通过.state拿到需要共享的属性
        // 在vue中有个util的工具类,通过defineReactive 方法,可以快速将某个数据变成双向绑定
        //这个方法接收三个参数:给哪个对象添加属性,给指定对象添加什么属性,要给属性添加什么值
        // =========================================这里已经安装了根模块的数据,=========================
        Vue.util.defineReactive(this, 'state', options.state)
        // 将传递进来的getters放到Store上
        this.initGetters(options)
        // 将传递进来的mutations放到Store上
        this.initMutions(options)
        // 将传递进来的actions放到Store上
        this.initActions(options)
        //提取模块信息
        this.modules = new ModuleCollection(options)
        // console.log(this.modules);
        // ========================================这里只需要将子模块数据安装==============================
        // 安装子模块数据
        this.initModules([], this.modules.root)
    }
    initModules(arr, rootModule) {
        // 用空数组判断是根模块还是子模块,如果是子模块就将数据安装到this.state上
        if (arr.length > 0) {
            let parent = arr.splice(0, arr.length - 1).reduce((state, currentKey) => {
                // console.log(state[currentKey]);
                return state[currentKey]
            }, this.state)
            // console.log(parent);
            // 哪一个对象添加属性,添加什么属性,什么数据
            Vue.set(parent,arr[arr.length-1],rootModule._state)
        }
        //如果当前不是子模块,那么需要从根模块中取出子模块的信息来安装
        for (let childrenModuleName in rootModule._children) {
            let childrenModule = rootModule._children[childrenModuleName]
            // console.log(childrenModule);
            this.initModules(arr.concat(childrenModuleName), childrenModule)
        }
    }
    dispatch = (type, payload) => {//asyncAddNum 10
        this.actions[type](payload)// this.actions['asyncAddNum'](10)
    }
    initActions(options) {
        // 1.拿到传递进来的actions
        let actions = options.actions || {}
        // 2.在Store上新增一个actions的属性
        this.actions = {}
        //3.遍历传递进来的mutation中的方法添加到当前Store的actions上
        for (let key in actions) {
            this.actions[key] = (payload) => {
                actions[key](this, payload)//actions['asyncAddNum'](this,10)
            }
        }
    }
    commit = (type, payload) => {//'addNum',10
        this.mutations[type](payload)
    }
    initMutions(options) {
        // 1.拿到传递进来的mutation
        let mutations = options.mutations || {}
        // 2.在Store上新增一个mutation的属性
        this.mutations = {}
        //3.遍历传递进来的mutation中的方法添加到当前Store的mutation上
        for (let key in mutations) {
            this.mutations[key] = (payload) => {//10
                // mutations['addNum'](num,10)
                mutations[key](this.state, payload)
            }
        }
    }
    initGetters(options) {
        // 1.拿到传递进来的getters
        let getters = options.getters || {}
        // 2.在Store上新增一个getters的属性
        this.getters = {}
        //3.遍历传递进来的getters中的方法添加到当前Store的getters上
        for (let key in getters) {
            //    console.log(key);
            Object.defineProperty(this.getters, key, {
                get: () => {
                    return getters[key](this.state)
                }
            })
        }
    }
}

//暴露
export default {
    install,
    Store
}

效果 在这里插入图片描述

8.安装模块方法

import Vue from 'vue'
//外界调用vue.use会执行install,并且会在执行时会把Vue实例和可选参数传递过来
const install = (Vue, options) => {
    // 给每一个Vue实例添加一个$store属性
    // vue中有一个名称叫做mixin的方法,这个方法会在创建每一个vue实例时执行
    // 所以可以通过mixin给每一个实例添加$store属性,mixin接收一个对象
    Vue.mixin({
        // 重写每一个vue实例的生命周期方法
        beforeCreate() {
            // Vue在创建实例的时候会先创建父组件,然后创建子组件
            // console.log(this.$options.name);
            // Root->App->Helloworld
            //如果是根组件,那么默认有store,所以只需要将store变成$store即可
            if (this.$options && this.$options.store) {
                this.$store = this.$options.store
            } else {
                // 如果不是根组件,默认没有store,将父亲的$store赋值给子代
                this.$store = this.$parent.$store
            }
        }
    })
}
// 定义模块信息的类
class ModuleCollection {
    constructor(rootModule) {
        // 独立的方法来格式化
        this.register([], rootModule)
    }
    register(arr, rootModule) {
        // console.log(arr);//["login", "account"]
        // console.log(rootModule.state);
        // console.log(rootModule);
        // 1.按照需要的格式创建模块
        let module = {
            _raw: rootModule,
            _state: rootModule.state,
            _children: {}
        }
        //2.保存模块信息
        // 判断是根模块还是子模块
        if (arr.length == 0) {
            this.root = module//保存根模块
        } else {
            //保存子模块
            // this.root._children[arr[arr.length-1]]=module
            // let testArr= ["login", "account"]
            // let res=testArr.splice(0,testArr.length-1)
            //     // console.log(res);
            let parent = arr.splice(0, arr.length - 1).reduce((root, currentKey) => {
                return root._children[currentKey]
            }, this.root)
            parent._children[arr[arr.length - 1]] = module
        } // 3.处理子模块,遍历跟模块中的modules
        for (let childrenModuleName in rootModule.modules) {
            let childrenModule = rootModule.modules[childrenModuleName]
            // console.log(childrenModule);
            // 开始递归,将子模块格式处理好后添加到children
            // concat() 方法用于连接两个或多个数组。
            this.register(arr.concat(childrenModuleName), childrenModule)
        }
    }
}
// 在Vuex中新增Store属性,这个属性的取值是一个类
class Store {
    // 调用的时候会传递一个对象参数,也就是仓库
    constructor(options) {
        // this.options = options
        // 将创建Store时需要共享的数据添加到Store上面
        //这样将来就可以通过this.$store拿到这个Store
        // 既然能拿到这个Store, 就可以通过.state拿到需要共享的属性
        // 在vue中有个util的工具类,通过defineReactive 方法,可以快速将某个数据变成双向绑定
        //这个方法接收三个参数:给哪个对象添加属性,给指定对象添加什么属性,要给属性添加什么值
        Vue.util.defineReactive(this, 'state', options.state)

        //提取模块信息
        this.modules = new ModuleCollection(options)
        // console.log(this.modules);
        // 安装子模块数据
        this.initModules([], this.modules.root)
    }
    initModules(arr, rootModule) {
        // 用空数组判断是根模块还是子模块,如果是子模块就将数据安装到this.state上
        if (arr.length > 0) {
            let parent = arr.splice(0, arr.length - 1).reduce((state, currentKey) => {
                // console.log(state[currentKey]);
                return state[currentKey]
            }, this.state)
            // console.log(parent);
            // 哪一个对象添加属性,添加什么属性,什么数据
            Vue.set(parent, arr[arr.length - 1], rootModule._state)
        }
        // =======================更新=========================
        // 将传递进来的getters放到Store上
        this.initGetters(rootModule._raw)
        // 将传递进来的mutations放到Store上
        this.initMutions(rootModule._raw)
        // 将传递进来的actions放到Store上
        this.initActions(rootModule._raw)
        //如果当前不是子模块,那么需要从根模块中取出子模块的信息来安装
        for (let childrenModuleName in rootModule._children) {
            let childrenModule = rootModule._children[childrenModuleName]
            // console.log(childrenModule);
            this.initModules(arr.concat(childrenModuleName), childrenModule)
        }
    }
    dispatch = (type, payload) => {//asyncAddNum 10
        // =======================更新=========================
        this.actions[type].forEach(fn => fn(payload))// this.actions['asyncAddNum'](10)
    }
    initActions(options) {
        // 1.拿到传递进来的actions
        let actions = options.actions || {}
        // 2.在Store上新增一个actions的属性
        // =======================更新=========================
        this.actions = this.actions || {}
        //3.遍历传递进来的mutation中的方法添加到当前Store的actions上
        for (let key in actions) {
            // =======================更新=========================
            this.actions[key] = this.actions[key] || []
            this.actions[key].push((payload) => {
                actions[key](this, payload)//actions['asyncAddNum'](this,10)
            })
        }
    }
    commit = (type, payload) => {//'addNum',10
        this.mutations[type].forEach(fn => fn(payload))
    }
    initMutions(options) {
        // 1.拿到传递进来的mutation
        let mutations = options.mutations || {}
        // 2.在Store上新增一个mutation的属性
        // =======================更新=========================
        this.mutations = this.mutations || {}
        //3.遍历传递进来的mutation中的方法添加到当前Store的mutation上
        for (let key in mutations) {
            // =======================更新=========================
            this.mutations[key] = this.mutations[key] || []
            this.mutations[key].push((payload) => {//10
                // mutations['addNum'](num,10)
                mutations[key](options.state, payload)
            })
        }
    }
    initGetters(options) {
        // 1.拿到传递进来的getters
        let getters = options.getters || {}
        // 2.在Store上新增一个getters的属性
        // =======================更新=========================
        this.getters = this.getters || {}
        //3.遍历传递进来的getters中的方法添加到当前Store的getters上
        for (let key in getters) {
            //    console.log(key);
            Object.defineProperty(this.getters, key, {
                get: () => {
                    // =======================更新=========================
                    return getters[key](options.state)
                }
            })
        }
    }
}

//暴露
export default {
    install,
    Store
}

效果
在这里插入图片描述

版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: 山竹回家了 原文链接:https://juejin.im/post/6926359064242814983

回到顶部