手写vuex

86 阅读1分钟

需求分析

  • 实现插件
    • 实现Store类
      • 维持一个响应式状态state
      • 实现commit()
      • 实现dispatch()
      • getters
    • 挂载$store

代码实现

1.创建src/kstore/index.js

import Vue from 'vue'
import Vuex from './kvuex.js'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    counter: 0
  },
  getters: {
    doubleCounter(state){
      return state.counter * 2
    }
  },
  mutations: {
    // state怎么来的
    add(state, num = 1){
      state.counter += num
    }
  },
  actions: {
    // 为什么能解构出commit? 上下文context是什么?  store实例
    add({ commit, dispatch, getters, state}){
      setTimeout(()=>{
        commit("add")
      },1000)
    }
  },
  modules: {
  }
})

2.创建src/kstore/kvuex.js

// 1.实现插件
let _Vue
class Store {
    constructor(options) {
        this._mutations = options.mutations
        this._actions = options.actions
        this._wrappedGetters = options.getters

        // 定义computed选项
        const computed = {}
        this.getters = {}
        const store = this
        Object.keys(this._wrappedGetters).forEach(key => {
            // 获取用户定义的getter
            const fn = store._wrappedGetters[key]
            // 转换为computed可以使用无参数形式
            computed[key] = function () {
                return fn(store.state)
            }
            // 为getters定义只读属性
            Object.defineProperty(store.getters, key, {
                get: () => store._vm[key]
            })
        })

        // 创建响应式的state
        // this.$store.state.xx
        this._vm = new _Vue({
            data() {
                return {
                    // 不希望被代理,就加上$或者_  vue内部隐藏属性检测
                    $$state: options.state
                }
            },
            computed
        })
        //修改this指向
        this.commit = this.commit.bind(this)
        this.dispatch = this.dispatch.bind(this)
    }

    get state() {
        return this._vm._data.$$state
    }

    set state(v) {
        console.error('please use replaceState to reset state');
    }
    // 修改state
    // this.$store.commit('add', 1)
    commit(type, payload) {
        //获取type对应的mutation
        const fn = this._mutations[type]
        if (!fn) {
            console.error('unknow mutation');
            return
        }
        // 传入state作为参数
        fn(this.state, payload)
    }
    dispatch(type, payload) {
        //获取type对应的action
        const fn = this._actions[type]
        if (!fn) {
            console.error('unknow action');
            return
        }
        // 传入当前Store实例this做上下文 所以能结构出store的state/commit/dispatch/getters
        return fn(this, payload)
    }
}

function install(Vue) {
    _Vue = Vue

    Vue.mixin({
        beforeCreate() {
            if (this.$options.store) {
                Vue.prototype.$store = this.$options.store
            }
        },
    })
}

// 导出的对象就是Vuex
export default {
    Store,
    install
}

3.main.js引入 import store from './kstore'