Vuex原理

268 阅读2分钟

Vuex是什么?

简单来说:Vuex是个状态仓库,存放着共用的状态信息(例如:用户信息)

与全局对象数据不同

  • Vuex的状态是响应式的,一处更改了,使用状态的组件也会更新
  • 不能直接更改state中的值,需要通过mutations中的函数(commit

着手实现

创建Vue项目,安装Vuex;在是src文件下的store文件夹中index.js文件能看到

Vue.use(Vuex);
​
export default new Vuex.Store({
    ...
});
  • Vuex导出了一个Store类
  • Vuex中存在install函数

新建一个XlVuex.js文件: 结构如下

class Store {
    // options 是new Vuex.Store中的参数对象
    constructor(options) {}
}
​
// Vue使用install,第一个参数为Vue对象
const install = (Vue) => {};
​
export default {
  install,
  Store,
};

Vuex是一个状态管理树,modules模板也会统一整理到主状态中;因此先把modules中的属性统一到主状态中

完整代码

git码云

modules

模板之间也存在着嵌套,所以需要递归将所有被应用的模板添加进来

class Store {
  constructor(options) {
    this.getModules(options);
  }
    
  getModules(options) {
    let modules = options.modules || {};
    // 把模板中对象的状态合并到主支上
    Object.keys(modules).forEach((module) => {
      options.state[module] = modules[module].state || {};
      options.getters[module] = modules[module].getters || {};
      options.mutations[module] = modules[module].mutations || {};
      options.actions[module] = modules[module].actions || {};
      // 存在引用就开始递归
      if(modules[module].modules) {
        this.getModules(modules[module])
      }
    });
  }
}

state

Vuex中只有state里面是数据,绑定到Vue的data里面才能达到双向绑定的效果 双向绑定需要将state状态树放入到Vue的data属性中

let _Vue; // 全局变量,获取到install传来的Vue对象
const install = (Vue) => {
  _Vue = Vue;
  _Vue.mixin({
    // 组件创建前给大家挂载上$store仓库
    beforeCreate() {
      if (this.$options && this.$options.store) {
        this.$store = this.$options.store;
      } else {
        // 子组件同样挂载
        this.$store = this.$parent && this.$parent.$store;
      }
    },
  });
};
​
class Store {
  constructor(options) {
     ...
    this._s = new _Vue({
      data: {
        state: options.state,
      },
    });
  }
  // 访问state的时候返回双向绑定中的state
  get state() {
    return this._s.state;
  }
    ...
}
​

getters

getters对象中存放着映射函数,使用到对应属性执行对应函数即可

class Store {
  constructor(options) {
     ...
    this.getters = {}; //在vuex中挂载getters对象
​
    // 没有定义getters就给个空,获取到实例传进来的getters
    let getters = options.getters || {};
​
    // 遍历获取的getters对象,并将对应方法绑定到挂载的 this.getters上
    Object.keys(getters).forEach((getter) => {
      Object.defineProperty(this.getters, getter, {
        // 调用挂载的getters自动调用
        get: () => {
          // 箭头函数保证this指向当前对象
          return getters[getter](this.state);
        },
      });
    });
  }
    ...
}

mutations

mutations同样存放着映射函数,不过与getters不同的是,需要使用commit指令进行操作

class Store {
  constructor(options) {
     ...
    this.mutations = {}; // 挂载
​
    let mutations = options.mutations || {};
    Object.keys(mutations).forEach((mutation) => {
      // 给挂载的mutations添加属性对应的函数
      this.mutations[mutation] = (payload) => {
        mutations[mutation](this.state, payload);
      };
    });
    ...
    }
   ...
  // mutations 函数指令
  commit(fnKey, payload) {
    this.mutations[fnKey](payload);
  }
}

actions

actions和mutations是类似的,不过actions是通过dispatch指令执行的

class Store {
  constructor(options) {
     ...
    this.actions = {};
​
    let actions = options.actions || {};
    for (let action in actions) {
      this.actions[action] = (payload) => {
        actions[action](this, payload);
      };
    }
    ...
    }
   ...
  // actions 函数指令
  dispatch(fnKey, payload) {
    this.actions[fnKey](payload);
  }
}

实现效果

将每个模块整合在一起,一个假的vuex就诞生了;是不是很简单呢!

下面展示一下个人效果: 在views文件夹下的Home.vue中

<template>
  <div class="home">
    {{$store.state.BuyCart.user.name}}
    <h1>{{$store.getters.ballCount}}</h1>
    <button @click="add">加一个</button>
    <button @click="asyncAdd">延迟加五个</button>
    <ul>
      <li v-for="item, i in $store.state.BuyCart.prodList" :key="i">{{item.name}}</li>
    </ul>
  </div>
</template><script>
export default {
  name: 'Home',
  methods: {
    add() {
      this.$store.commit("increment")
    },
    asyncAdd() {
      this.$store.dispatch('asyncIncrement', 5)
    }
  },
}
</script>

store文件夹下index.js

import Vue from "vue";
// import Vuex from "vuex";
import Vuex from "./XlVuex"
import BuyCart from "./buyCart"Vue.use(Vuex);
​
export default new Vuex.Store({
  state: {
    count: 0,
  },
  getters: {
    ballCount: state => state.count + "个球"
  },
  mutations: {
    increment(state, payload=1) {
      state.count += payload;
    },
  },
  actions: {
    asyncIncrement(state, payload) {
      setTimeout(() => {
        state.commit("increment", payload);
      }, 1000);
    },
  },
​
  modules: {
    BuyCart
  },
});
​

image-20211221164141817.png

其他可以自行添加

效果图

image-20211221164216837.png