15-Vuex

362 阅读4分钟

Vuex是什么?

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

在组件这边对状态进行操作时,首先是分发到Actions然后再提交到Mutations,为什么不直接提交到Mutations?

因为在与服务器交互的时候可能存在异步的行为,如果存在异步行为一定要通过Dispatch分发到Actions然后再提交到Mutations,Actions中声明的方法是异步的,Mutations中声明的方法是同步的,这Vuex的一个约定,修改状态的唯一方法就是提交Mutations。

什么情况使用Vuex?

Vuex 可以帮助我们管理共享状态(这个数据属性想共享在所有组件中使用),并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。

如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。

Vuex的基本使用

安装

在已经创建的项目中通过vue add vuex进行安装

使用Vue-Cli3创建项目

终端输入vue create 项目名,选择Babel、Router、Vuex就行。

 (*) Babel
>(*) Router
 (*) Vuex

项目目录

在当前项目目录下相比vue-router项目多了一个store文件夹。同时,在main.js中进行一个导入import store from './store'并在new Vue({store})进行挂载,然后在store目录下的index.js中对vuex进行一个导入import Vuex from 'vuex'并使用插件Vue.use(Vuex),最后创建并抛出:

export default new Vuex.Store({
  state: { //当前的状态
  },
  mutations: { //声明同步的方法
  },
  actions: { //声明异步的方法
  },
  modules: {}
})

在state中声明我们想要共享的数据,然后在组件中的computed方法中通过this.$store.state.数据名进行获取:

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

使用vuex来进行一个计数操作

在About.vue中声明方法,通过this.$store.dispatch("increate");触发actions中的方法,然后在actions中commitmutations中的方法,在mutations的方法中通过state.count++;对状态进行修改。

如果修改state中的状态是不是异步操作,可以直接commit这个mutations中的方法,如果涉及到了异步操作,一定要先dispatch这个actions中的方法再commit。

store下的index.js:

关于actions中第一个参数的解构,第一个参数是context官方运行上下文,获取当前的vuex实例,只不过这边使用的时候通过解构取值获取{commit} = context

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: { //当前的状态
    count: 0
  },
  mutations: { //声明同步的方法
    increate(state) {
      //修改状态
      state.count++;
    },
    decrease(state) {
      state.count--;
    },
    //异步方法 需要在actions中声明
    /* increateAsync(state) {
      setTimeout(() => {
        state.count++;
      }, 1000)
    } */
    increateAsync(state) {
      state.count++;
    }
  },
  actions: { //声明异步的方法
    //解构赋值 获取 commit
    //非异步操作可以直接 commit  mutations中的方法
    /* increate({
      commit
    }) {
      // commit  mutations中的方法
      commit('increate')
    },
    decrease({
      commit
    }) {
      commit('decrease')
    } */

    //异步操作
    increateAsync({
      commit
    }) {
      setTimeout(() => {
        commit('increateAsync')
      }, 1000)
    }
  },
  modules: {}
})

About.vue

<template>
  <div class="about">
    <h1>This is an about page</h1>
    {{count}}
    <button @click="increate">+1</button>
    <button @click="decrease">-1</button>
    <button @click="increateAsync">+1(异步)</button>
  </div>
</template>
<script>
export default {
  computed: {
    count() {
      return this.$store.state.count;
    },
  },
  methods: {
    increate() {
      // dispatch 触发 actions 中声明的方法
      //this.$store.dispatch("increate");

      //因为这两个方法是同步的,所以可以直接 commit
      this.$store.commit("increate");
    },
    decrease() {
      //this.$store.dispatch("decrease");
      this.$store.commit("decrease");
    },
    increateAsync() {
      this.$store.dispatch("increateAsync");
    },
  },
};
</script>

Vuex的辅助函数

在项目中有很多的状态,在组件中如果每一个都通过像计数操作中this.$store.state.count;这样去获取,会很麻烦,为了解决这个问题,vuex提供了辅助函数

首先需要在组件中对辅助函数进行一个导入

//导出vuex中的辅助函数
import { mapState, mapGetters, mapMutations, mapActions } from "vuex";

mapState

之前我们是在computed属性中通过count() {return this.$store.state.count;},这种方式进行一个获取,有了辅助函数后可以配合es6中的扩展运算符...进行一个非常简便的获取。

  1. 在当前组件中如果computed中的属性名和state中的状态名一样,可以通过下面这种方式获取state中定义的countusername状态。
...mapState(["count", "username"]),
  1. 在当前组件中如果computed中的属性名和state中的状态名不一样,则需要通过下述方式获取,其中myCountuser就是当前组件定义在computed中的属性,countusername则是state种定义的状态。
...mapState({
      myCount: "count",
      user: "username",
    }),

mapGetters

在index.js中new Vuex.Store({})除了常用的4个属性之外,其实还有可选的getters,在getters中可以定义一些方法,然后在组件那边可以通过mapGetters进行获取。

index.js

getters: {
    evenOrOdd(state) {
      return state.count % 2 === 0 ? '偶数' : '奇数';
    }
  },

About.vue

export default {
  computed: {
  //不用辅助函数获取
    /*  evenOrOdd() {
      return this.$store.getters.evenOrOdd;
    }, */
    
    //使用mapGetters获取
    ...mapGetters(["evenOrOdd"]),
  },

mapMutations

mapMutations则是对同步方法进行获取。对于之前计数操作中的同步方法:

  methods: {
    /* increate() {
      // dispatch 触发 actions 中声明的方法
      //this.$store.dispatch("increate");

      //因为这两个方法是同步的,所以可以直接 commit
      this.$store.commit("increate");
    }, */
    /* decrease() {
      //this.$store.dispatch("decrease");
      this.$store.commit("decrease");
    }, */
    //获取同步方法
    ...mapMutations(["increate", "decrease"]),
  },

mapActions

mapActions是对异步方法进行获取。对于之前计数操作中的异步方法:

  methods: {
    /* increateAsync() {
      this.$store.dispatch("increateAsync");
    }, */
    //获取异步方法
    ...mapActions(["increateAsync"]),
  },

在组件内提交数据给Vuex

dispatch、commit内传参

在About.vue中触发函数的时候同时进行传参,可以是字符串数组对象,一般情况下传递对象

// 传参 方式一
increateAsync() {
	  //在组件内部提交数据  载荷形式分发
      this.$store.dispatch("increateAsync", { amount: 10 });
    },
    
// 传参 方式二
increateAsync() {
      this.$store.dispatch({
        type: "increateAsync",
        amount: 10,
      });
    },

上述两种方式均可,然后在index.js中需要进行第二个参数的接收

 mutations: { //声明同步的方法
    increateAsync(state, amount) {
      state.count += amount;
    }
  },
  
    actions: { //声明异步的方法
    //异步操作
    increateAsync({
      commit
    }, {
      amount
    }) {
      setTimeout(() => {
        commit('increateAsync', amount)
      }, 1000)
    }
  },

辅助函数传参

对于辅助函数来说也能够进行一个参数的传递,只不过它需要在绑定click事件的时候就进行一个传参。

<button @click="increateAsync(amount)">+1(异步)</button>

Vuex模块化

当项目足够大时,在index.js的state属性中就可能需要定义很多的状态,这会使得代码显得非常臃肿,难以阅读,不易维护。Vuex给我们提供了一个modules属性,运行模块化创建,每一个模块都维护它自己的statemutationsactions等。

模块化步骤

  1. 在store文件夹下创建一个modules文件夹,对每一个模块在modules文件夹下分别创建对应的js文件。

cart.js

export default {
//命名空间
    namespaced: true,
    state: {
        cartList: [],
        count: 0
    },
    getters: {},
    mutations: {},
    actions: {}
}

此时当前模块和index.js还没有任何关系。

  1. 将创建的模块导入到index.js中,并将导入模块挂载到modules属性中
//导入
import cart from './modules/cart';
import products from './modules/products';

//挂载
  modules: {
    cart,
    products
  }

命名空间

为了模块的维护性和复用性,可以在模块中添加namespaced: true,使其成为带命名空间的模块,当模块被注册后,它的所有getter、actions及mutations都会自动根据当前模块注册的路径调整命名。对于模块内的state,模块内的状态已经是嵌套的了,使用namespaced属性不会对其产生影响,命名空间主要是针对于模块中的辅助函数起作用。

在组件中获取模块数据

cart.js模块中定义了

    getters: {
        getCount(state) {
            return state.count;
        }
    },
  1. 首先在组件中进行辅助函数导入
//导入辅助函数
import { mapGetters } from "vuex";
  1. 通过辅助函数获取模块中定义的方法

这边与之前稍有不同,第一个参数需要指明是哪个模块,比如下面从cart模块中获取getters中的getCount方法。

  computed: {
    ...mapGetters("cart", ["getCount"]),
  },