前端架构学习笔记(二)封装一个简单的 Vuex

前端架构学习笔记(二)封装一个简单的 Vuex

如果有问题,或者有更好的解决方案,欢迎大家分享和指教。

一、最终目标

  • 在任意组件中可以使用 $store 访问 store 中的数据
  • state 都是响应式数据
  • 实现通过 commit 调用 mutations
  • 实现通过 dispatch 调用 actions
  • 实现 getters,并借用 computed 计算属性缓存 getters 的值(getters) 也是响应式数据

二、目录

src 文件夹下新建以下文件

my-store
  store.js  // 封装 store 
  index.js  // 配置 store

my-store/index.js 中的配置

import Vue from 'vue'
import Vuex from './store'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    counter: 0,
  },
  mutations: {
    add(state) {
      state.counter++;
    }
  },
  actions: {
    add({commit}) {
      setTimeout(() => {
        commit('add');
      }, 1000);
    }
  },
  getters: {
    doubleCounter(state) {
      return state.counter * 2;
    },
    troubleCounter(state) {
      return state.counter * 3;
    }
  }
})

main.js 中做如下配置

import Vue from 'vue'
import App from './App.vue'
import store from './my-store/index'

Vue.config.productionTip = false

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

希望最终能完成 state 的调用,赋值,动态响应,以及 getters 的使用:

<!-- 任意 vue 组件中做如下调用 -->
<button @click="$store.commit('add')">同步改变 count</button>
<button @click="$store.dispatch('add')">异步改变 count</button>
<h3>{{ $store.state.counter }}</h3>
<h3>{{ $store.getters.doubleCounter }}</h3>
<h3>{{ $store.getters.troubleCounter }}</h3>

三、封装(my-store/store.js)

/*
* 1. 插件:挂载 $store
* 2. 实现一个数据管理类 store
*  */

let Vue;

function install(_Vue) {
  Vue = _Vue;

  // 全局混入,将 $store  绑定给 Vue 原型,以便所有组件都能正常访问 $store
  Vue.mixin({
    beforeCreate() {
      if(this.$options.store) {
        Vue.prototype.$store = this.$options.store;
      }
    }
  });
}

class Store {
  constructor(options) {
    const {state, mutations, actions, getters} = options;
    /* 1. 将用户传入的 state 声明成响应式数据 */
    /* *
    * 借用 Vue 内部将组件实例的 data 转换成响应式数据的功能,将 state 转换成响应式数据(其实是借助 Observer 类)
    * 可以 log 一下 vuex 的 state,走的的确也是 Observer 类
    *
    * 这里直接暴露 state,在实际的封装中是不可取的,可以为 state 设置一个 setter,和 getter
    *
    * this.state = new Vue({
    *   data: state || {}
    * });
    */

    /* 2. 处理 mutations */
    this._mutations = mutations || {};

    /* 3. 处理 actions */
    this._actions = actions || {};

    /* 5. 处理 getters */
    this._wrapperGetters = getters || {};
    this.getters = {};
    // 借用 computed 实现值的缓存
    const computed = this.registerGetters(state, this);
    this._vm = new Vue({
      data: {
        $$state: state
      },
      computed
    });

    /* 4. 确定 this 指向,在确保 commit 和 dispatch 的时候 this 指向不会出问题 */
    this.commit = this.commit.bind(this);
    this.dispatch = this.dispatch.bind(this);

    /* 5. 处理 getters  */
  }

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

  set state(v) {
    console.error('place user replaceState to reset state');
  }

  commit(type, payload) {
    const entry = this._mutations[type];
    if(!entry) {
      console.error('unkown mutation type ' + type);
    }

    entry(this.state, payload);
  }

  dispatch(type, payload) {
    const entry = this._actions[type];
    if(!entry) {
      console.error('unkown action type ' + type);
    }

    entry(this, payload);
  }

  /* 将 getters 注册成响应式数据 */
  registerGetters(state, _this) {
    const computed = {};
    Object.keys(_this._wrapperGetters).forEach(key => {
      const fn = _this._wrapperGetters[key];
      // 将用户传入的 getters 的键,一次绑定给 _vm 的 computed,实现值的缓存功能
      computed[key] = function() {
        return fn(state);
      };

      Object.defineProperty(_this.getters, key, {
        //  getters 中都是只读值
        get() {
          // 取值时,根据 Vue实例的规则,会从 _vm 的 computed 中取到值,并且返回
          return _this._vm[key];
        }
      });
    });
    return computed;
  }
}

export default { Store, install }

扫码拉你进入前端技术交流群

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

锋利的二丫

如果对您有帮助,请博主喝杯奶茶

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值