手写vuex

1.Vuex特点1

使用Vuex的时候需要用到Vue的use方法
我们都知道use方法是用于注册插件的
所以VueX的本质就是一个插件
所以实现VueX就是在实现一个全局共享数据的插件

Vue-plugin开发插件

2.Vuex特点2

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

// Nuex.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  // 用于保存全局共享数据
  state: {
  },
  // 用于同步修改共享数据
  mutations: {
  },
  // 用于异步修改共享数据
  actions: {
  },
  // 用于模块化共享数据
  modules: {
  }
})

3.Vuex特点3

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

4.手写Vuex

import Vue from 'vue'
const install = (Vue, options)=>{
    // 给每一个Vue实例都添加一个$store属性
    /**
     * 在vue中有一个mixin方法,这个方法会在创建每一个Vue实例的时候执行
     * 所以可以通过mixin方法给每一个Vue实例添加$store属性
     */
    Vue.mixin({
    	// 重写
        beforeCreate(){
       		 /**
             * Vue在创建实例的时候会先创建父组件,然后再创建子组件
             */
            // console.log(this.$options.name);
            // Root === App == helloworld
            /**
             * 如果是根组件,name默认就有store
             * 只需要将store变成$store即可
             */
            if(this.$options && this.$options.store){
                this.$store = this.$options.store;
            }
            else{
            	/**
                 * 如果不是根组件,那么默认没有store
                 * 只需要将它父组件的$store赋值给它即可
                 */
                this.$store = this.$parent.$store; // 子组件的$store === 父组件的store
            }
        }
    });
}
class ModuleCollection {
    constructor(rootModule){
        this.register([], rootModule);
    }
    register(arr, rootModule){
        // console.log(arr); // [] [home] [account] [account, login]
        // 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 = ['account', 'login'];
            // let res = testArr.splice(0, testArr.length - 1);
            // console.log(res);
            // ['a', 'b', 'c'] -> ['a', 'b']
            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.处理子模块
        for(let childrenModuleName in rootModule.modules){
            let childrenModule = rootModule.modules[childrenModuleName];
            this.register(arr.concat(childrenModuleName) ,childrenModule)
        }
    }
}
class Store {
    constructor(options){
    	// this.opions = options
        // 将创建Store的时候需要共享的数据添加到Store上面
        // 将来就能通过this.$store拿到这个Store
        // 能拿到这个Store,就可以通过 .state拿到需要共享的属性
        // this.state = options.state // 无法实现双向绑定数据
        // ------------------------------------------------
        // 将传递进来的state放到Store上
        Vue.util.defineReactive(this, 'state', options.state);
        // 提取模块信息
        this.modules = new ModuleCollection(options);
        // 安装子模块的数据
        this.initModules([], this.modules.root);
        /*
        模块化
        ModuleCollection方法将数据处理成下面这个格式:
        let root = {
            _raw: rootModule,
            _state: rootModule.state,
            _children: {
                home:{
                    _raw: homeModule,
                    _state: homeModule.state,
                    _children: {}
                },
                account:{
                    _raw: accountModule,
                    _state: accountModule.state,
                    _children: {
                        login: {
                            _raw: loginModule,
                            _state: loginModule.state,
                            _children: {}
                        }
                    }
                },
            }
        }
        * */
    }
    initModules(arr, rootModule){
        // console.log(arr); // [] [home] [account] [account, login]
        // 如果当前是子模块, 那么就需要将数据安装到this.state上
        if(arr.length > 0){
            let parent = arr.splice(0, arr.length-1).reduce((state, currentKey)=>{
                return state[currentKey];
            }, this.state);
            Vue.set(parent, arr[arr.length-1], rootModule._state);
        }
        // 将传递进来的getters放到Store上
        this.initGetters(rootModule._raw);
        // 将传递进来的mutations放到Store上
        this.initMutations(rootModule._raw);
        // 将传递进来的actions放到Store上
        this.initActions(rootModule._raw);
        // 如果当前不是子模块, 那么就需要从根模块中取出子模块的信息来安装
        for(let childrenModuleName in rootModule._children){
            let  childrenModule = rootModule._children[childrenModuleName];
            this.initModules(arr.concat(childrenModuleName), childrenModule);
        }
    }
    dispatch = (type, payload)=>{
        this.actions[type].forEach(fn=>fn(payload));
    }
    initActions(options){
        // 1.拿到传递进来的actions
        let actions = options.actions || {};
        // 2.在Store上新增一个actions的属性
        this.actions = this.actions || {};
        // 3.将传递进来的actions中的方法添加到当前Store的actions上
        for(let key in actions){
            this.actions[key] = this.actions[key] || [];
            this.actions[key].push((payload)=>{
                actions[key](this, payload);
            });
        }
    }
    commit = (type, payload)=>{
        // console.log(this);
        // 允许相同的方法名,调用时,全部执行
        this.mutations[type].forEach(fn=>fn(payload));
    }
    initMutations(options){
        // 1.拿到传递进来的mutations
        let mutations = options.mutations || {};
        // 2.在Store上新增一个mutations的属性
        this.mutations = this.mutations || {};
        // 3.将传递进来的mutations中的方法添加到当前Store的mutations上
        for(let key in mutations){
        	// 允许相同的方法名,数组将所有的方法保存
            this.mutations[key] = this.mutations[key] || [];
            this.mutations[key].push((payload)=>{
                mutations[key](options.state, payload);
            });
        }
    }
    initGetters(options){
        // this.getters = options.getters;
        // 1.拿到传递进来的getters
        let getters = options.getters || {};
        // 2.在Store上新增一个getters的属性
        this.getters = this.getters || {};
        // 3.将传递进来的getters中的方法添加到当前Store的getters上
        for(let key in getters){
            Object.defineProperty(this.getters, key, {
                get:()=>{
                    return getters[key](options.state);
                }
            })
        }
    }
}
export default {
    install,
    Store
}

使用

import Vue from 'vue'
// import Vuex from 'vuex'
import Vuex from './Nuex'
Vue.use(Vuex)
let home = {
  state: {
    name: 'www'
  },
  getters: {
    getHomeName(state){
      return state.name + '222222';
    },
    // 多个模块中不能出现同名的getters方法
    // getGlobalName(state){
    //   return state.globalName + '111111';
    // }
  },
  mutations: {
    changeHomeName(state, payload){
      state.name += payload;
    },
    // 注意点: 多个模块的mutations中可以出现同名的方法
    //         多个同名的方法不会覆盖, 会放到数组中然后依次执行
    changeGlobalName(state, payload){
      console.log('home中的changeGlobalName');
      state.globalName += payload;
    }
  },
  actions: {
    asyncChangeHomeName({commit}, payload){
      setTimeout(()=>{
        commit('changeHomeName', payload);
      }, 1000);
    },
    // 注意点: 多个模块的actions中可以出现同名的方法
    //         多个同名的方法不会覆盖, 会放到数组中然后依次执行
    asyncChangeGlobalName({commit}, payload){
      console.log('home中的asyncChangeGlobalName');
      setTimeout(()=>{
        commit('changeGlobalName', payload);
      }, 1000);
    }
  }
}
let login = {
  state: {
    name: 'com'
  },
  getters: {
    getLoginName(state){
      return state.name + '333333';
    }
  },
  mutations: {
    changeLoginName(state, payload){
      state.name += payload;
    }
  },
  actions: {
    asyncChangeLoginName({commit}, payload){
      setTimeout(()=>{
        commit('changeLoginName', payload);
      }, 1000);
    }
  },
}
let account = {
  state: {
    name: 'it666'
  },
  getters: {
    getAccountName(state){
      return state.name + '333333';
    }
  },
  mutations: {
    changeAccountName(state, payload){
      state.name += payload;
    }
  },
  actions: {
    asyncChangeAccountName({commit}, payload){
      setTimeout(()=>{
        commit('changeAccountName', payload);
      }, 1000);
    }
  },
  modules: {
    login: login
  }
}
export default new Vuex.Store({
  // 用于保存全局共享数据
  state: {
    globalName: 'lnj'
  },
  getters: {
    getGlobalName(state){
      return state.globalName + '111111';
    }
  },
  // 用于同步修改共享数据
  mutations: {
    changeGlobalName(state, payload){
      console.log('全局中的changeGlobalName');
      state.globalName += payload;
    }
  },
  // 用于异步修改共享数据
  actions: {
    asyncChangeGlobalName({commit}, payload){
      console.log('全局中的asyncChangeGlobalName');
      setTimeout(()=>{
        commit('changeGlobalName', payload);
      }, 1000);
    }
  },
  // 用于模块化共享数据
  modules: {
    home:home,
    account: account
  }
})

4.Vue.util.defineReactive

Vue.util.defineReactive(参数1, 参数2, 参数3)

在Vue中有一个util的工具类,在这个工具类上有一个defineReactive
通过这个方法就可以快速的将某个数据变成双向绑定的数据

defineReactive这个方法接收三个参数

  • 第一个参数:要给哪个对象添加属性
  • 第二个参数:要给指定的对象添加什么属性
  • 第三个参数:要给这个属性添加什么值

5.Vue.set

Vue.set( target, propertyName/index, value )
  • 第一个参数:给哪个对象添加属性
  • 第二个参数:添加的什么属性
  • 第三个参数:添加什么数据

Vue.set官网介绍
在这里插入图片描述


学习记录❥(^_-)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值