58 如果让你从零开始写一个vuex,说说你的思路

思路分析

这个题目很有难度,首先思考vuex解决的问题:存储用户全局状态并提供管理状态API。

  • vuex需求分析
  • 如何实现这些需求

回答范例

  1. 官方说vuex是一个状态管理模式和库,并确保这些状态以可预期的方式变更。可见要实现一个vuex
  • 要实现一个Store存储全局状态
  • 要提供修改状态所需API:commit(type, payload), dispatch(type, payload)
  1. 实现Store时,可以定义Store类,构造函数接收选项options,设置属性state对外暴露状态,提供commitdispatch修改属性state。这里需要设置state为响应式对象,同时将Store定义为一个Vue插件
  2. commit(type, payload)方法中可以获取用户传入mutations并执行它,这样可以按用户提供的方法修改状态。 dispatch(type, payload)类似,但需要注意它可能是异步的,需要返回一个Promise给用户以处理异步结果

实践

Store的实现:

    class Store {
        constructor(options) {
            this.state = reactive(options.state)
            this.options = options
        }
        commit(type, payload) {
            this.options.mutations[type].call(this, this.state, payload)
        }
    }

vuex简易版

    /**
     * 1 实现插件,挂载$store
     * 2 实现store
     */
    
    let Vue;
    
    class Store {
      constructor(options) {
        // state响应式处理
        // 外部访问: this.$store.state.***
        // 第一种写法
        // this.state = new Vue({
        //   data: options.state
        // })
    
        // 第二种写法:防止外界直接接触内部vue实例,防止外部强行变更
        this._vm = new Vue({
          data: {
            $$state: options.state
          }
        })
    
        this._mutations = options.mutations
        this._actions = options.actions
        this.getters = {}
        options.getters && this.handleGetters(options.getters)
    
        this.commit = this.commit.bind(this)
        this.dispatch = this.dispatch.bind(this)
      }
    
      get state () {
        return this._vm._data.$$state
      }
    
      set state (val) {
        return new Error('Please use replaceState to reset state')
      }
    
      handleGetters (getters) {
        Object.keys(getters).map(key => {
          Object.defineProperty(this.getters, key, {
            get: () => getters[key](this.state)
          })
        })
      }
    
      commit (type, payload) {
        let entry = this._mutations[type]
        if (!entry) {
          return new Error(`${type} is not defined`)
        }
    
        entry(this.state, payload)
      }
    
      dispatch (type, payload) {
        let entry = this._actions[type]
        if (!entry) {
          return new Error(`${type} is not defined`)
        }
    
        entry(this, payload)
      }
    }
    
    const install = (_Vue) => {
      Vue = _Vue
    
      Vue.mixin({
        beforeCreate () {
          if (this.$options.store) {
            Vue.prototype.$store = this.$options.store
          }
        },
      })
    }
    
    
    export default { Store, install }

验证方式

    import Vue from 'vue'
    import Vuex from './vuex'
    // this.$store
    Vue.use(Vuex)
    
    export default new Vuex.Store({
      state: {
        counter: 0
      },
      mutations: {
        // state从哪里来的
        add (state) {
          state.counter++
        }
      },
      getters: {
        doubleCounter (state) {
          return state.counter * 2
        }
      },
      actions: {
        add ({ commit }) {
          setTimeout(() => {
            commit('add')
          }, 1000)
        }
      },
      modules: {
      }
    })
Last Updated:
Contributors: leeguooooo