vuex源码实现

223 阅读1分钟

1.使用

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    counter: 0
  },
  mutations: { 
    add(state) {
      state.counter++
    }
  },
  actions: { 
    add({ commit }) {
      setTimeout(() => {
        commit('add')
      }, 1000);
    }
  },
  modules: {
  }
})

<template>
  <div id="app">
    <p @click="$store.commit('add')">counter: {{$store.state.counter}}</p>
    <p @click="$store.dispatch('add')">async counter: {{$store.state.counter}}</p>
  </div>
</template>

2.代码实现

let _Vue
class Store {

    constructor(options) {
        this._mutations = options.mutations
        this._actions = options.actions
        this._getters = options.getters
        this.getters = {}
        //利用vue的实例 实现响应式
        this._vm = new _Vue({
            data() {
                return {
                    $$state: options.state //添加$符号的 数据不能被外部直接用 _vm.$$state
                }
            },
        })
        this.commit = this.commit.bind(this) //bind之后必须赋值,不然this的绑定无效
        this.dispatch = this.dispatch.bind(this) 
    }
    //使用es6 的get set方法实现数据的有效保护
    get state() {//由于不能直接用_vm.$$state访问 ,所以需要this._vm._data.$$state
        return this._vm._data.$$state
    }

    set state(val) {
        console.error("不能直接设置state")
    }

    commit(type , playload){
       const fn = this._mutations[type] 
       console.log(fn)
       if (!fn) {
        //    throw Error('unknow mutation')
       }else {
        fn( this.state, playload)
       }
    }
    dispatch(type , playload){
        const fn = this._actions[type]
        if (!fn) {
            // throw Error('unknow action')
        } else {
            return fn(this,playload)
        }
    }
 
}

function install(Vue) {
    _Vue = Vue //记录全局实例
    Vue.mixin({
        beforeCreate(){
            if(this.$options.store) {
                Vue.prototype.$store = this.$options.store
            } 
        }
    })
}
 
export default {
    Store,
    install
}

3.getters 实现


//使用 
Vue.use(Vuex)

export default new Vuex.Store({
    state : {
        counter : 2
    }, 
    getters : {
        total(state) {
            return "合计:" + state.counter
        }
    }
})
<template>
  <div id="app"> 
    <p >getters total: {{$store.getters.total}}</p>
  </div>
</template>


 constructor(options) {
        this._getters = options.getters
        this.getters = {}
        let _this = this 
        for (const key in this._getters) { 
                const fn = this._getters[key];   
                Object.defineProperty(this.getters,key, {
                    get : () => {
                        return fn(_this.state)
                    }
                })
        }
    }

4.getters 缓存实现


 this._getters = options.getters
        this.getters = {}

        let _this = this
        let computed = {}
        for (const key in this._getters) { 
                const fn = this._getters[key]
                computed[key] = function() {
                    return fn(_this.state)
                }
                Object.defineProperty(this.getters,key, {
                    get : () => {
                        return _this._vm[key]
                    }
                })
        } 

        //利用vue的实例 实现响应式
        this._vm = new _Vue({
            data() {
                return {
                    $$state: options.state //添加$符号的 数据不能被外部直接用 _vm.$$state
                }
            },
            computed //实现利用vue的computed实现缓存 
        })