mini-vuex的基本实现

221 阅读1分钟

1.思考:为啥vuex必须和vue使用而redux并不需要

vuex需要依赖vue是因为vuex是依赖vue而做到的响应式数据

但是redux是通过观察者的模式去实现的

2.vuex里如何实现响应式数据

通过借鸡生蛋的方式达到实现响应式数据

3.首先我们需要按照vue插件的格式要求写

//myVuex/myVuex.js
let Vue
class Store {
  constructor(){
   
  }

}
const install = function(vue_){
   Vue = vue_
   Vue.mixin({
     beforeCreate(){
        if(this.$options.store){
         Vue.prototype.$store = this.$options.store //只是把store挂载在vue的原型链上
        }
     }
   })
}
export default {Store,install}

4.定义一个store

//myVuex/index.js
import Vue from 'vue'
import Vuex from './myVuex.js'
Vue.use(Vuex)
const store = new Vuex.Store({
  state:{
    count : 1,
    name : '我是高复古'
  },
  mutations:{
    add(state,data){
      state.count = state.count + data
    }
  },
  actions:{
    add({commit},data){
      setTimeout(() => {
        commit('add',2)
      },1000)
    }
  },
  getters:{
    addCount(state){
      return state.count+2
    }
  }
})
export default store
//main.js
import App from 'App.js'
import store from 'myVuex/index.js'
new Vue({
 store,
 render: h => {
    return h(App)
  },
}).$mount('#app')
//App.vue
<template>
 <div @click="$store.commit('add',2)">
  {$store.state.count}   
  </div>
  <div @click="$store.dispatch('add',2)">
    {$store.state.count}   
  </div>
  <div>
    {this.$store.$getters.addCount}
  </div>
</template>
<script>
  export default {
    name:'App'
  }
</script>

如何通过借鸡生蛋达成修改store的数据变为响应式数据

//myVuex/myVuex.js
let Vue
class Store {
  constructor({state,getters,mutations,actions}){
   //一般我们通过Vue.utils.defineReactive(this,'state',state)来生成响应式数据
   //但是这里我们借用借鸡生蛋来做,是因为我们并不想用户直接可以修改state的数据
   this._actions = actions 
   this._mutations = mutations
   this._vm = new Vue({ //通过创建实例达到借鸡生蛋的目的
     data(){
       return {
         $$state:state //$$可以防止代理
       }
     }
   })
  }
  get state(){
    return this._vm._data.$$state
  }
  set state(value){
     throw Error('请不要直接修改state')
  }
  mutations(action,data){
     const fn = this._mutations[action]
     if(!fn){
        thorw error('你需要将此方法定义在你的actions里')
     }else{
        fn(this._vm._data.$$state,data)//需要将state传入进去
     }
  }
}
const install = function(vue_){
   Vue = vue_
   Vue.mixin({
     beforeCreate(){
        if(this.$options.store){
         Vue.prototype.$store = this.$options.store //只是把store挂载在vue的原型链上
        }
     }
   })
}
export default {Store,install}

dispatch的实现

dispatch做的事情其实很简单就是通过action去找到对应的函数并把当前store的控制权给下去,需要特别注意由于dispatch在各种函数里调用,this指针可能会乱,所以我们需要使用bind绑定下this

  class Store {
  constructor({state,getters,mutations,actions}){
   //一般我们通过Vue.utils.defineReactive(this,'state',state)来生成响应式数据
   //但是这里我们借用借鸡生蛋来做,是因为我们并不想用户直接可以修改state的数据
   this._actions = actions 
   this._mutations = mutations
   this._vm = new Vue({ //通过创建实例达到借鸡生蛋的目的
     data(){
       return {
         $$state:state //$$可以防止代理
       }
     }
   })
   this.dispatch = this.dispatch.bind(this)
   this.commit = this.commit.bind(this)
  }
  get state(){
    return this._vm._data.$$state
  }
  set state(value){
     throw Error('请不要直接修改state')
  }
  dispatch(action,data){
     const fn = this._actions[action]
     if(!fn){
        thorw error('你需要将此方法定义在你的actions里')
     }else{
        fn(this,data)//需要将state传入进去
     }
  },
  mutations(action,data){
     const fn = this._mutations[action]
     if(!fn){
        thorw error('你需要将此方法定义在你的actions里')
     }else{
        fn(this._vm._data.$$state,data)//需要将state传入进去
     }
  }
}

commit实现

和dispatch差不多就是传入的参数不一样,这个传入的是state

  class Store {
  constructor({state,getters,mutations,actions}){
   //一般我们通过Vue.utils.defineReactive(this,'state',state)来生成响应式数据
   //但是这里我们借用借鸡生蛋来做,是因为我们并不想用户直接可以修改state的数据
   this._actions = actions 
   this._mutations = mutations
   this._vm = new Vue({ //通过创建实例达到借鸡生蛋的目的
     data(){
       return {
         $$state:state //$$可以防止代理
       }
     }
   })
   this.dispatch = this.dispatch.bind(this)
   this.commit = this.commit.bind(this)
  }
  get state(){
    return this._vm._data.$$state
  }
  set state(value){
     throw Error('请不要直接修改state')
  }
  dispatch(action,data){
     const fn = this._actions[action]
     if(!fn){
        thorw error('你需要将此方法定义在你的actions里')
     }else{
        fn(this,data)//需要将state传入进去
     }
  },
  mutations(action,data){
     const fn = this._mutations[action]
     if(!fn){
        thorw error('你需要将此方法定义在你的actions里')
     }else{
        fn(this._vm._data.$$state,data)//需要将state传入进去
     }
  }
}

getters的实现

也是通过vue实现,通过computed完成

首先我们需要将getters上的方法挂载在computed上,

其次我们需要将对应的值$store.getters.xxx对应成借鸡生蛋里的computed的属性值

  class Store {
  constructor({state,getters,mutations,actions}){
   //一般我们通过Vue.utils.defineReactive(this,'state',state)来生成响应式数据
   //但是这里我们借用借鸡生蛋来做,是因为我们并不想用户直接可以修改state的数据
   this._actions = actions 
   this._mutations = mutations
   this.getters = getters
   const computed = {}
   const store = this
   //遍历getters将方法挂载在computed
   Object.keys(this.getters).forEach(key => {
   const fn = this.getters[key]
     computed[key] = () => fn(this._vm._data.$$state) //computed的属性需要是个函数,所以需要这边使用高级函数
     Object.defineProperty(this.getters,key,{
       get(){
         return store._vm[key]
       }
     })
   })
   this._vm = new Vue({ //通过创建实例达到借鸡生蛋的目的
     data(){
       return {
         $$state:state //$$可以防止代理
       }
     }
   })
   this.dispatch = this.dispatch.bind(this)
   this.commit = this.commit.bind(this)
  }
  get state(){
    return this._vm._data.$$state
  }
  set state(value){
     throw Error('请不要直接修改state')
  }
  dispatch(action,data){
     const fn = this._actions[action]
     if(!fn){
        thorw error('你需要将此方法定义在你的actions里')
     }else{
        fn(this,data)//需要将state传入进去
     }
  },
  mutations(action,data){
     const fn = this._mutations[action]
     if(!fn){
        thorw error('你需要将此方法定义在你的actions里')
     }else{
        fn(this._vm._data.$$state,data)//需要将state传入进去
     }
  }
}