likes
comments
collection
share

【Vuex原理】从零实现一个基础版Vuex

作者站长头像
站长
· 阅读数 53

回顾vuex使用

  • 创建store/index.js
  • 使用Vuex.store()创建vuex实例
  • 使用Vue.use(Vuex)注册Vuex
  • 使用new Vue({store})使用vuex
  • 然后就可以在组件中使用this.$store访问共享的数据了,以及调用mutationsactions去修改共享数据。

实现state

vuex作为一个插件,需要在vue中使用,需要按照vue插件开发规范去实现。在vue文档中写到,注册插件需要使用提供一个install方法。

  • mixin可以将一些函数合并到每个组件的生命周期中,所以利用beforeCreate将共享数据store挂载到每个组件上。
  • 只有跟组件上可以访问this.$options.strore,又因为父组件比子组件先创建,为了让子组件也可以访问store,所以使用this.$parent.$store进行store绑定。
  • 然后就可以像使用vuex一样,使用myVuex进行数据共享。
// MyVuex.js
const install = (Vue, options) => {
  Vue.mixin({
    beforeCreate() {
      // 将store挂载到每个vue组件上
      if (this.$options?.store) {
        this.$store = this.$options.store;
      } else {
        this.$store = this.$parent.$store
      }
    }
  })
}
class Store {
  constructor(options) {
    // 将传递进来的state挂载到this中
    this.state = options.state;
  }
}
export default {
  install,
  Store
}

实现getters

在vue组件中,可以使用this.$store.getters.xxx来访问共享的store中的xxx变量。

修改Store类

  • store对象添加一个getters的空对象属性,并使用Object.defineProperty方法将监听store.getters的属性,当触发get的时候,就会将调用options.getter上对应的方法,并将store.state传递给该函数。
class Store {
  constructor(options) {
    // 将传递进来的state放到Store上
    this.state = options.state;
    // 1.拿到传递进来的getters
    let getters = options.getters || {};
    // 2.在Store上新增一个getters的属性
    this.getters = {};
    // 3.将传递进来的getters中的方法添加到当前Store的getters上
    for (let key in getters) {
      Object.defineProperty(this.getters, key, {
        get: () => {
          return getters[key](this.state);
        }
      })
    }
  }
}

实现mutations

mutations用于同步修改store中的数据,使用this.$store.commit来执行该同步函数。

  • 创建initMutations函数与commit函数
  • initMutations函数中,再根据options上面传递过来的mutations,为store对象添加一个mutations空对象,并为其添加方法,并将store上的state传递给该方法。所以开发者在使用的使用就可以得到state对象。并在构造函数中执行。
  • 创建commit方法,使得开发者可以通过this.$store.commit来访问store上的值。

注意

问题:这时候我们会发现,执行了commit方法后,页面并没有发生变化,但是打印发现数据确实变了,这是因为没有将视图与数据进行双向绑定。

解决:需要将this.state = options.state;替换为Vue.util.defineReactive(this, 'state', options.state)。这是vue提供的一个可以将视图与数据进行绑定的api。它可以将我们的state与每个vue组件进行绑定,实现视图根据实际实时更新。

// MyVuex.js

const install = (Vue, options) => {
  Vue.mixin({
    beforeCreate() {
      // 将store挂载到每个vue组件上
      if (this.$options?.store) {
        this.$store = this.$options.store;
      } else {
        this.$store = this.$parent.$store
      }
    }
  })
}
class Store {
  constructor(options) {
    // 将传递进来的state放到Store上
    // this.state = options.state;
    Vue.util.defineReactive(this, 'state', options.state);
    // 将传递进来的getters放到Store上
    this.initGetters(options);
    // 将传递进来的mutations放到Store上
    this.initMutations(options);
  }
  commit(type, payload) { // 'addNum', 10
    this.mutations[type](payload); //  this.mutations[addNum](10);
  }
  initMutations(options) {
    // 1.拿到传递进来的mutations
    let mutations = options.mutations || {};
    // 2.在Store上新增一个mutations的属性
    this.mutations = {};
    // 3.将传递进来的mutations中的方法添加到当前Store的mutations上
    for (let key in mutations) {
      this.mutations[key] = (payload) => {
        mutations[key](this.state, payload);
      }
    }
  }
}
export default {
  install,
  Store
}

实现actions

actions是用于创建异步修改store中数据的方法,通过this.$store.dispatch可以执行异步函数修改store

  • 创建dispath函数与initActions函数
  • initActions中,根据options中传递过来的actions,为store创建一个actions空对象,并根据options.actions,为store.actons添加方法,并将this作为参数传递给action方法,这样用户在定义actions方法的时候就可以拿到commit方法了。
  • dispatch中,根据函数名,调用store.actons上对应的方法。

注意:需要使用箭头函数,防止开发者在外部定义的actions方法拿不到this

dispatch = (type, payload) => { // 'asyncAddAge', 5
    this.actions[type](payload); // this.actions[asyncAddAge](5);
}
initActions = (options) => {
    // 1.拿到传递进来的actions
    let actions = options.actions || {};
    // 2.在Store上新增一个actions的属性
    this.actions = {};
    // 3.将传递进来的actions中的方法添加到当前Store的actions上
    for(let key in actions){
        this.actions[key] = (payload)=>{ // 5
            actions[key](this, payload); // asyncAddAge(this, 5);
        }
    }
}

完整代码

import Vue from 'vue'
const install = (Vue, options) => {
  Vue.mixin({
    beforeCreate() {
      if (this.$options?.store) {
        this.$store = this.$options.store;
      } else {
        this.$store = this.$parent.$store;
      }
    }
  });
}
class Store {
  constructor(options) {
    Vue.util.defineReactive(this, 'state', options.state);
    this.initGetters(options);
    this.initMutations(options);
    this.initActions(options);
  }
  dispatch = (type, payload) => {
    this.actions[type](payload);
  }
  initActions(options) {
    let actions = options.actions || {};
    this.actions = {};
    for (let key in actions) {
      this.actions[key] = (payload) => {
        actions[key](this, payload);
      }
    }
  }
  commit = (type, payload) => {

    this.mutations[type](payload);
  }
  initMutations(options) {

    let mutations = options.mutations || {};
    this.mutations = {};
    for (let key in mutations) {
      this.mutations[key] = (payload) => {
        mutations[key](this.state, payload);
      }
    }
  }
  initGetters(options) {
    let getters = options.getters || {};
    this.getters = {};
    for (let key in getters) {
      Object.defineProperty(this.getters, key, {
        get: () => {
          return getters[key](this.state);
        }
      })
    }
  }
}
export default {
  install,
  Store
}

点赞收藏不迷路

转载自:https://juejin.cn/post/7157616962074312734
评论
请登录