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

55 阅读1分钟

思路分析

这个题目很有难度,首先思考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)
     }
 }

/**
  * 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: {
   }
 })