同样,这一次我们只讲原理,如果你还不会Vuex的使用,简易移步官网

看文档

任务分析

先来根据Vuex的流程图来看一下我们需要做什么

image-20210419080742142

  • vuex也是一个插件
  • 实现四个东西:state/mutations/actions/getters
  • 创建Store
  • 数据响应式

插件

关于插件这里同样不多说,不会的请移步官网,这里依旧贴一张图

cahjian

在安装插件时,通过Vue.use触发插件的install方法

function install(_Vue) {
  Vue = _Vue
  Vue.mixin({
    beforeCreate() {
      // 只有根元素才有store
      if (this.$options.store) {
        Vue.prototype.$store = this.$options.store
      }
    }
  })
}

这里注意只有第一次调用时才给元素添加store,也就是根节点

四个核心

首先要有一个可实例化的Store类,这点官网给的实例可以看出来

image-20210419202657828

那我们就先创建一个Store类,它接收一个配置对象

class Store {
  constructor(options) {
    this.state = new Vue({
      data() {
        return { ...options.state}
      }
    })
    this.mutations = options.mutations
    this.actions = options.actions
    options.getters && this.handleGetters(options.getters)
  }
}

state负责存储数据,借用Vue实例来实现响应式;mutations用来存储改变state的方法;actions用来存储异步方法,这里注意actions本身并不具备异步特性,仅作为异步方法的容器,他可以调用mutations中的方法;getters可以用来读取state的内容,将结果处理返回

class Store {
  constructor(options) {
    this.state = new Vue({
      data() {
        return { ...options.state}
      }
    })
    this.mutations = options.mutations
    this.actions = options.actions
    options.getters && this.handleGetters(options.getters)
  }
  
  commit = (type, args) => {
    this.mutations[type](this.state, args)
  }

  dispatch = (type, args) => {
    this.actions[type]({
      commit: this.commit,
      state: this.state
    }, args)
  }
  handleGetters(getters) {
    this.getters = {}
    Object.keys(getters).forEach(key => {
      Object.defineProperty(this.getters, key, {
        get: () => {
          return getters[key](this.state)
        }
      })
    })
  }
}

测试

现在我们已经实现了一个简易版的Vuex,我们接下来进行一下测试

新建一个store.js,用来实例化store

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    add(state) {
      state.count++
    }
  },
  actions: {
    addAsync({ commit }) {
      setTimeout(() => {
        commit('add')
      }, 1000)
    }
  },
  getters: {
    getCount(state) {
      return state.count
    }
  }
})

在main.js中引入store

import store from './store'

new Vue({
  store,
  router,
  render: h => h(App),
}).$mount('#app')

编写Vue组件,调用Vuex方法

<div>
  {{$store.getters.getCount}}
  <br />
  <button @click="$store.commit('add')">同步+1</button>
  <button @click="$store.dispatch('addAsync')">延时+1</button>
</div>

测试结果如下

QQ录屏20210419203459


前端小白