zoukankan      html  css  js  c++  java
  • 手写Vuex

    手写Vuex

    需求分析

    • Vue.use(Vuex) => 这是一个插件,需要实现一个静态方法 install
    • new Vuex.Store(options) => 需要实现一个Store类
    • 页面中可以通过 this.$store.xxx 可以访问store实例 => 需要挂载$store到Vue.prototype上
    • 页面中可以通过 this.$store.state.xxx可以动态的更新页面 => state是响应式对对象
    • 页面中可以通过 `this.$store.getters.getCounter可以动态的更新页面 => getters是响应式对对象,getters.xxx是响应式的函数取值 => 考虑computed属性
    • 实现2个方法 commit、dispatch方法

    代码实现

    1. main.js
    import Vue from 'vue'
    import App from './App.vue'
    import store from './store'
    
    new Vue({
      store,
      render: h => h(App)
    }).$mount('#app')
    
    1. store/index.js
    import Vue from 'vue'
    import Vuex from './KVuex'
    
    Vue.use(Vuex)
    
    new Vuex.Store({
      state: {
        counter: 1
      },
      mutations: {
        syncAdd(state, multiple){
          state.counter += multiple
        }
      },
      actions: {
        asyncAdd({ commit }, multiple){
          setTimeout(() => {
            commit('syncAdd',multiple)
          },300)
        }
      },
      getters: {
        getCounter: state => state.counter,
        getCounterByDivisor(state){ 
          return divisor => state.counter/ divisor
        },
        getCounter3: state => divisor => state.counter / divisor
      }
    })
    
    1. 页面中展示
    <p>{{ $store.state.counter }}</p>
    <p @click="$store.commit('syncAdd(2)')"> 同步提交数据 </p>
    <p @click="$store.dispatch('asyncAdd(4)')"> 异步提交数据 </p>
    <p>{{ $store.getters.getCounterByDivisor(4) }}</p>
    
    1. 代码展示
    // kVuex需求分析
    // 1. new Vuex.Store() =>  实现一个Store类,而且还不能直接导出Store类,反而要导出一个对象,对象里有Store
    // 2. Vue.use(Vuex) => 插件,要实现一个install方法, install 方法不是Store的静态方法,而是对象Vuex的 静态方法
    // 3. vue文件中通过 this.$store 访问store实例 => 挂载$store到原型上
    // 4. 在main.js中,先①引入store实例的代码,再②new Vue()传入store参数
    // => ①import store时,先进行Vue.use(Vuex),即执行install方法,在该方法中进行$store挂载: this.$options.store && (Vue.prototype.$store = this.$options.store), 如果this.$options.store存在,就进行挂载。this.$options.store 这里是new Vue({store})传入的options,这个传参是在②中执行的,现在还在执行①的代码,就想把②的代码拿来用
    // 解决方案:
    // ① 挂载$store放在setTimeout中,延迟执行 => 代码low、延迟时间不可控
    // ② 使用Vue.mixin,在beforeCreate钩子函数中进行执行挂载,在beforeCreate执行时,new Vue({store})是一定执行了的,不然进不了这个钩子函数
    // 5. 页面中 this.$store.state.counter 是响应式的 =>  state是响应式数据
    // 6. commit、dispatch、getter方法
    // 7. 页面中 this.$store.getters.getCounter 是响应式的 => getters是响应式数据, 而且,getters里面存储的是方法,考虑使用computed计算属性来实现
    
    let Vue;
    
    class Store {
      constructor(options) {
        const store = this;
        this.$options = options;
        this._mutations = options.mutations;
        this._actions = options.actions;
        this._wrappedgetters = options.getters;
        this.getters = {};
        // computed : {foo(){}}没有参数的函数。而getter是有参数的
        const computed = {};
        Object.keys(this._wrappedgetters).forEach((key) => {
          computed[key] = function () {
            return store._wrappedgetters[key](store.state);
          };
          // 设置getters[key],只读属性。 并且利用computed计算属性来获取getters[key]的值,且为响应式
          Object.defineProperty(store.getters, key, {
            get() {
              return store._vm[key];
            },
          });
        });
        // 响应式数据 state
        // ① Vue.util.defineReactive => 是用来定义对象的属性为响应式
        // ② 借鸡生蛋 vue实例的data对象是响应式的
        // this.state = options.state
        // this.state = new Vue({ data: options.state }); // this.state.counter => vue实例.counter
        // 将state保护起来
    
        // 响应式getters是响应式、方法 => computed计算属性来实现
        // this.$store.getters.getCounter / this.$store.getters.getCounterById
        this._vm = new Vue({
          data: {
            $$state: options.state, // 在data中定义 $$xxx, 在实例中是获取不到该值的,被保护起来了。可以通过 this.$data.$$state获取
          },
          computed,
        });
    
        // 给commit的this指向绑定到store实例上
        // this.commit = this.commit.bind(store); 这个跟下面的函数是一个意思,只是call执行起来性能比bind好一点
        // bind、call、apply都是用来改变函数执行时的上下文的。
        // 性能(时间消耗从少到多): call > bind > apply 但是,如果我们需要传入数组,即使有es6的解构的方法,apply的性能还是要优于call/bind
        // ① bind : Function.bind(obj, arg1, arg2, arg3, ……) => 返回值是函数,需要手动调用
        // ① call : Function.call(obj, arg1, arg2, arg3, ……) => apply、call是立即调用
        // ① apply : Function.apply(obj, [argArray]) => call新能要优于apply,可能是因为apply解析数组的时候要耗费性能?
        const { commit, dispatch } = store;
        this.commit = function boundCommit(type, payload) {
          commit.call(store, type, payload);
        };
    
        this.dispatch = function boundDispatch(type, payload) {
          return dispatch.call(store, type, payload);
        };
      }
    
      get state() {
        return this._vm._data.$$state;
      }
    
      commit(type, payload) {
        const entry = this._mutations[type];
        if (entry) {
          entry(this.state, payload);
        }
      }
    
      // dispatch('setCounter', payload)
      // addAsync({ commit }, payload) {
      //   setTimeout(() => {
      //     commit("addSync", payload); // 实际应用的时候是不
      //   });
      // }
      // 注意this指向问题
      dispatch(type, payload) {
        const entry = this._actions[type];
        if (entry) {
          // ① 考虑到异步操作,有可能会返回promise,进行操作,所以要return 一下
          // ② 在entry内部执行的时候,如果有setTimeout等,就会涉及到this指向问题,所以需要把 commit函数的this绑定到该store实例上。
          return entry(this, payload); // 异步
        }
      }
    }
    
    const install = function (_Vue) {
      Vue = _Vue;
      Vue.mixin({
        beforeCreate() {
          // this执行vue实例
          if (this.$options.store) {
            // 说明是根Vue,实例化中传入的参数
            Vue.prototype.$store = this.$options.store;
          }
        },
      });
    };
    
    const Vuex = {
      Store,
    };
    
    Vuex.install = install;
    
    export default Vuex;
    
    
  • 相关阅读:
    【Wyn Enterprise BI知识库】 什么是商业智能 ZT
    Wyn BI的机会在哪里:越靠近消费者的行业,比如零售、文娱和金融,信息化投入越大 ZT
    客户化软件时代的前夜 ZT
    在“非软件企业”开发软件的困局 ZT
    行业观察报告:从SAAS困局看行业趋势 ZT
    超级干货 :一文读懂数据可视化 ZT
    传统BI还是自助式BI---BI与数据分析 ZT
    【BI学习笔记】在Linux上安装Wyn Enterprise商业智能报表服务器
    MAMP 配置: Mac with OSX 10.8 + (Mac + Apache + MySQL + Php)
    Emule Xtreme Kid eD2K 设置
  • 原文地址:https://www.cnblogs.com/shine-lovely/p/14870951.html
Copyright © 2011-2022 走看看