Vue的状态管理 - Vuex

vuex的相关知识点不多,但较为难理解,很多没有做过项目的同学去看官方文档可能无法真正理解,所以我这里除了介绍其基本的安装使用之外,会从他的应用场景及使用vuex的好处等方面来聊一下个人浅见!

vuex是什么?

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

我个人的理解是,如果有一些公共的数据需要在多个组件中共享或者某一个状态的改变会影响多个组件,那么这时候用vuex是非常合适的,比如我们经常会看到的中后台都有多风格的切换,这种全局的改变就可以用到vuex去完成!

vuex的基本使用

  1. 安装
npm install --save vuex

 

  1. main.js中引入
import store from "./store";

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

 

  1. 在我们用vue/cli创建的项目的store/index.js中引入如下内容
import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {},
  mutations: {},
  actions: {},
  modules: {}
});

vuex的五个核心

vuex中有五个核心,即以上代码中展示的 stategettermutationsactionsmodules

state

state: 定义了应用状态的数据结构,可以在这里设置默认的初始状态。

老规矩,翠花上代码

在store/index.js中的state中传入一个键值对 counter: 10,这个键值对就记录了一个counter的公共的数据初始状态!

export default new Vuex.Store({
  state: {
    counter: 10
  },
  ...
});

组件中通过 $store.state.counter 即可调用到conter的值,但一般我们会将这个状态放在计算属性当中!

以我们的项目为例,在src/components的路径下有一个HelloWorld.vue的组件,我们来通过封装一个count的计算属性来调用下counter的值:

老规矩,翠花上代码

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <p>{{ count }}:计算属性 </p>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  props: {
    msg: String
  },
  computed:{
    count(){
      return this.$store.state.counter
    }
  }
};
</script>

 

getters

getters: 实际上是store的计算属性,类似于computed,对state里的数据进行一些过滤,改造等等,如果state中的值需要通过某种变换,才能传给用户查看,那么就可以在这里定义方法进行转换!

假设我们要在state.counter的基础上派生出一个新的状态powercount出来,这个状态需要counter相乘的结果, 我们就可以这样做!

  • getters 接受 state 作为其第一个参数:
export default new Vuex.Store({
  state: {
    counter: 10
  },
  getters: {
    pownercount(state){
      return state.counter * state.counter
    }
  },
});

在组件中可以通过$store.getters.pownercount获取新状态的值,继续在计算属性computed中构造一个powerconter方法,在组建中直接调用{{ powerconter }}即可!

<script>
export default {
  name: "HelloWorld",
  props: {
    msg: String
  },
  computed:{
    count(){
      return this.$store.state.counter
    },
    powerconter(){
      return this.$store.getters.pownercount
    },
  },
};
</script>

 

mutations

mutations: 提供修改 store中的状态的入口,必须是同步函数,也就是我们需要根据某些条件改变某个状态,即可在这里定义:

我们在 mutations 中定义了一个叫add的函数,函数体就是我们要进行更改的地方,会接受 state作为第一个参数,第二个是自定义传参,我们将状态counter的自增1:

export default new Vuex.Store({
  state: {
    counter: 10
  },
  getters: {
    pownercount(state){
      return state.counter * state.counter
    }
  },
  mutations: {
    add(state){
      return state.counter += 1
    }
  },
});

在组件中使用必须先声明一个方法,然后在methods中通过声明的方法加commit将这个方法传递过去:

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <p>{{ count }}:计算属性 </p>
    <!-- 点击count的值会自增1,调用的两种方式  -->
    <button @click="$store.commit('add')">add</button>
    <!-- 在methods中定义的方法  -->
    <button @click="addcount()">addcount</button>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  props: {
    msg: String
  },
  computed:{
    count(){
      return this.$store.state.counter
    },
    powerconter(){
      return this.$store.getters.pownercount
    },
  },
  methods:{
    addcount(){
      this.$store.commit('add');
    }
  }
};
</script>

为了方便随处调用我们一般在methods中定义一个方法使用!

<!-- 直接使用  -->
<button @click="$store.commit('add')">add</button>
<!-- 在methods中定义的方法  -->
<button @click="addcount()">addcount</button>

还可以通过在组件中引入vuex的mapMutations直接注册

<script>
// 引入mapMutations
import {mapMutations} from 'vuex'
export default {
  name: "HelloWorld",
  props: {
    msg: String
  },
  computed:{
    count(){
      return this.$store.state.counter
    },
    powerconter(){
      return this.$store.getters.pownercount
    },
  },
  methods:{
    ...mapMutations(['add'])
  }
};
</script>

调用:

<button @click="mutations中的方法名称('可带参数')">-</button>

actions

actions:当需要异步更改数据时,通过 Action 提交的是 mutation,而不是直接变更状态。

Action 类似于 mutation,不同在于:

  1. Action 提交的是 mutation,而不是直接变更状态。
  2. Action 可以包含任意异步操作。

老规矩,翠花上代码:

// store/index.js

export default new Vuex.Store({
  state: {
    counter: 10,
    // 新定义一个obj
    obj: {}
  },
  getters: {
    pownercount(state){
      return state.counter * state.counter
    }
  },
  mutations: {
    add(state){
      state.counter += 1;
    },
    // 修改state中的obj
    getparam(state, Object){
      state.obj = Object
    }
  },
  actions: {
    getparamsync(context, Object){
      // 模拟异步请求,延时3s
      setTimeout(()=>{
      	// 通过commit提交一个名为getparam的mutation
      	//action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation
        context.commit('getparam',Object)
      },3000)
    }
  },
});

在组建中通过$store.dispatch('increment')触发调用

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <button @click="getVal()">getobj</button>
    <p>{{ $store.state.obj }}</p>
  </div>
<template>

 methods:{
    ...
    getVal() {
    	  let name= 'xia';
    	  let age= '26';
    	  let sex= 'man';
    	  //1.通过dispatch将方法getParamSync和多个参数{name,age,sex}传递给actions
    	  this.$store.dispatch('getparamsync',{name,age,sex})
       },
    ...
  }

 

modules

由于 Vuex 使用单一状态树,应用的所有状态会集中到一个比较大的对象。Vuex 允许将 store 分割成模块( module )。每个模块拥有自己的 state、mutation、action、getter 甚至是嵌套子模块——从上至下进行同样方式的分割。

如下所示:

modules: {
    // 如果有多个可以在这里独立分隔为多个模块
    a: {
    // App中调用{{ $store.state.a }}
      state: {
        name: 'vue'
      },
    // 其余组建中调用与外侧调用一致
      mutations: {},
      actions: {},
      getters: {}
    },
    b: {

    }
  }

总结

最后再去看官方提供的 Vuex 的设计流程图,就豁然开朗了。

在 Vuex 中说白了,任何的操作都是围绕 state 来进行的,Vuex 是状态管理器,作用就是管理 state 中的状态,其他提供的所有功能 Getter、Mutation、Action 都是为了能够更好的管理 state,而之所以设计成期望通过 Mutation 改变状态,是因为我们期望所有状态的变化都是有迹可循的!