170行写一个vuex

Vuex源码不多,1000多行,排除各种报错,兼容,语法糖和拓展功能,其实就干了两件事:
1、把外部传入的store对象组织成了一棵树型的数据结构
2、把state和getters的数据变成响应式,保证界面中可以双向绑定

按照这个需求,结合vuex的实现思路自己写了个vuexx,个人一直不太接受vuex3里的面向对象组织形式,深入源码可以看到state和action、mutation等的组织方式是不太一样的,比如state.module.type 和 dispatch(‘module/type’),太蛋疼了。这块按照个人的理解重新实现了。

App.vue

<template>
  <div>
    vuexx
    <div>root: {{count}}</div>
    <div>rootPlus: {{countPlus}}</div>
    <div>A: {{countA}}</div>
    <div>A: {{countAPlus}}</div>
    <button @click="onClick">add</button>
  </div>
</template>

<script>
import store from './store'

export default {
  name: 'App',

  computed: {
    count () {
      return store.state.count
    },

    countPlus () {
      return store.getters.countPlus
    },

    countA () {
      return store.modules.A.state.count
    },

    countAPlus () {
      return store.modules.A.getters.countPlus
    }
  },

  methods: {
    onClick() {
      store.dispatch('add', 1)
      store.modules.A.dispatch('add', 1)
    }
  }
}
</script>

<style>

</style>

store.js

import Vue from 'vue'
import Vuexx from './lib'

Vue.use(Vuexx)

export default new Vuexx.Store({
  state: {
    count: 1
  },

  getters: {
    countPlus: state => state.count + 1
  },

  mutations: {
    add (state, payload) {
      state.count += payload
    }
  },

  actions: {
    add (context, payload) {
      context.commit('add', payload)
    }
  },

  modules: {
    A: {
      state: {
        count: 10
      },
    
      getters: {
        countPlus: state => state.count + 1
      },
    
      mutations: {
        add (state, payload) {
          state.count += payload
        }
      },
    
      actions: {
        add (context, payload) {
          context.commit('add', payload)
        }
      },    
    }
  }

})

vuexx

let Vue

function install (_Vue) {
  if (Vue && Vue === _Vue) {
    return 
  }

  Vue = _Vue
  applyMixin(Vue)
}

function applyMixin (Vue) {
  const version = Number(Vue.version.split('.')[0])

  if (version >= 2) {
    // 把$store挂载到各个vue实例中,意义不大,此处不实现了
  } else {
    // depracated
  }
}

function isPromise (val) {
  return val && typeof val.then === 'function'
}

function partial (fn, arg) {
  return function () {
    return fn(arg)
  }
}

class Store {
  constructor(options, parent) {
    this.committing = false
    this.state = options.state
    this.getters = options.getters
    this.mutations = options.mutations
    this.actions = options.actions
    this.parent = parent
    
    if (options.modules) {
      this.modules = {}
      Object.keys(options.modules).forEach(moduleName => {
        this.modules[moduleName] = new Store(options.modules[moduleName], this)
      })
    }

    this.installModule()
    this.resetStoreVM()
  }

  installModule() {
    this.registerMutations()
    this.registerActions()
    this.regsiterGetters()
  }

  resetStoreVM () {
    const oldVM = this._vm

    const computed = {}

    if (this.getters) {
      Object.keys(this.getters).forEach(key => {
        computed[key] = partial(this.getters[key], this)
        Object.defineProperty(this.getters, key, {
          get: () => {
            return this._vm[key]
          }
        })
      })
    }

    const silent = Vue.config.silent
    Vue.config.silent = true
    this._vm = new Vue({
      data: {
        $$state: this.state
      },
      computed: computed
    })
    Vue.config.silent = silent

    Object.defineProperty(this, 'state', {
      get: () => {
        return this._vm._data.$$state
      }
    })

    if (oldVM) {
      Vue.nextTick(() => {
        return oldVM.$destroy()
      })
    }
  }

  regsiterGetters () {
    if (this.getters) {
      const originGetters = this.getters
      this.getters = {}

      Object.keys(originGetters).forEach(key => {
        this.getters[key] = () => {
          return originGetters[key](this.state, this.getters)
        }
      })
    }
  }

  registerMutations () {
    if (this.mutations) {
      const orginMutations = this.mutations
      this.mutations = {}
    
      Object.keys(orginMutations).forEach(key => {
        this.mutations[key] = (state, payload) => {
          this.withCommit(() => {
            orginMutations[key].call(this, state, payload)
          })
        }
      })
    }
  }

  registerActions () {
    if (this.actions) {
      const originActions = this.actions
      this.actions = {}
      const context = this

      Object.keys(originActions).forEach(key => {
        this.actions[key] = (payload) => {
          let res = originActions[key].call(this, context, payload)
          if (!isPromise(res)) {
            res = Promise.resolve(res)
          }
          return res
        }
      }) 
    }
  }

  withCommit (fn) {
    const committing = this.committing
    this.committing = true
    fn()
    this.committing = committing
  }

  commit (type, payload) {
    const mutation = this.mutations[type]
    mutation.call(this, this.state, payload)
  }

  dispatch(type, payload) {
    const action = this.actions[type]
    if (action) {
      const res = action.call(this, payload)
      return new Promise((resolve, reject) => {
        res.then(res => {
          resolve(res)
        }, error => {
          reject(error)
        })
      })
    }
  }
}

export default {
  install,
  Store,
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值