Vuex状态管理

1,523 阅读3分钟

一、Vuex是什么?

专门在 Vue 中实现集中式状态(数据)管理的一个 Vue 插件,对 vue 应 用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方 式,且适用于任意组件间通信。其实可以理解 “共享” 数据。

二、Vuex的原理

1、vuex 原理

vuex 所有的数据操作必须通过 action -> mutation -> state(响应式数据) 的流程来进行,再结合 Vue 的数据视图双向绑定特性来实现页面的展示更新。

2、vuex原理图

原理/流程解析:

Vuex包含3个主要部分Actions、Mutations、State

  • Actions:用于响应组件中的动作,间接更新state(数据),包含同步/异步操作,支持多个同名方法,按照注册的顺序依次触发;

action 并不是直接修改数据,而是通过 mutations 去修改,这是需要注意的

  • Mutations:用于操作数据,直接操作更新state(数据),是Vuex修改state的唯一推荐方法,不可进行异步操作,且方法名只能全局唯一;
  • State:用于存储数据,页面上显示的所有数据从State对象中读取,方法名也是全局唯一;
  • Vue Components:Vue组件,页面上负责接收用户操作的交互行为,执行dispatch方法触发对应的Actions进行回应;
  • Dispatch:操作行为触发方法,是唯一能执行Actions的方法;
  • Commit:状态改变提交操作方法,对mutations进行提交,是唯一能执行mutations的方法;
  • Devtools:官方的调试工具,非常方便地去调试项目中Vuex的数据变化,主要用来检测Mutations的;
  1. Vue Components(组件),在组件中 $store . dispatch ( '对应的 action 回调名' ) 触发Actions来响应组件中的动作;
  2. 在Actions使用commit ( '对应的 mutations 方法名' ) 触发mutations;
  1. mutations内部操作数据State,之后Vue内部将操作后的数据render渲染到页面上。

可以通过这个通俗例子理解Vuex:

Vue Components相当于是去餐厅吃饭的客人,Actions相当于餐厅的服务员,Mutations相当于餐厅的后厨,菜好不好吃关键就在后厨,State相当于客人点的菜。

客人进门了说话(注:说话就相当于调用dispatch)要两份蛋炒饭(注:相当于dispatch('jia',2)),服务员接受信息并通过点菜系统提交菜单:两份蛋炒饭(注:服务员提交菜单相当调用commit,commit('JIA',2)),于是点菜信息传到了后厨,这个时候后厨将菜进行了一系列加工把菜(注:菜相当于State,加工相当修改State)做好了,最后呈现给客户。

如果客人和餐厅足够熟悉了,他可以不通过服务员,直接跟后厨点菜(注:也就是说Actions响应的是组件的动作,并不是必须的存在的,组件可以直接提交mutations,不需要经过actions这一步),只要一种情况必须通过服务员,那就是餐厅换了菜系,之前的菜都不上了,客人必须通过服务员才能点菜单(注:新菜单其实就相当于backend api 服务器里面的数据,可以异步操作).

三、Vuex的用法

  1. 在src下新建一个文件夹store,在store下创建一个文件index.js,基本代码如下

基本代码:

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

Vue.use(Vuex)
// 创建并暴露store
export default new Vuex.Store({
  // actions--用于响应组件中的动作(相当于:服务员)
  actions: {
    //...
  },
  // mutations--用于操作数据(相当于厨师)
  mutations: {
   //...
  },
  // state-- 用于存储数据(相当于:顾客点菜的菜单)
  state: {
    //..
  },
  // getters--用于将state中的数据进行加工(相当于计算属性)
  getters: {
    //..
  }
})

比如:

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

Vue.use(Vuex)
// 创建并暴露store
export default new Vuex.Store({
  // actions--用于响应组件中的动作(相当于:服务员)
  actions: {
    jiaOdd(context, value) {
      console.log(context, value);
      // context:vuex上下文,即整个vuex里面的配置,所有vuex里面的都可以使用
      // value:你的传参
      console.log('actions的jiaOdd被调用了');
      if (context.state.sum % 2) {
        context.commit('JIA', value)
      }
    },
  },
  // mutations--用于操作数据(相当于厨师)
  mutations: {
    JIA(state, value) {
      // value:你的传参
      console.log('mutations的JIA被调用了');
      state.sum += value
    },
  },
  // state-- 用于存储数据(相当于:顾客点菜的菜单)
  state: {
    sum: 0,//当前的和
  },
  // getters--用于将state中的数据进行加工(相当于计算属性)
  getters: {
    bigSum(state) {
      return state.sum * 10
    },
  }
})
  1. 在main.js中引入store

main.js:

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.config.productionTip = false

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')
  1. 在组件中使用
<template>
  <div>
    <h3>vuex学习:求和案例</h3>
    <p>当前求和:{{$store.state.sum}}</p>
    <select v-model.number="n">
      <option value="1">1</option>
      <option value="2">2</option>
      <option value="3">3</option>
    </select>
    <button @click="increment">+</button>
    <button @click="incrementOdd">当前求和为奇数再加</button>
  </div>
</template>

<script>
export default {
  data(){
    return{
      n:1//用户选择数字
    }
  },
  methods:{
    increment(){
      console.log('+');
      // 通过this.$store.commit调用mutations里的方法
      this.$store.commit('JIA',this.n)
    },
    incrementOdd(){
      console.log('当前求和为奇数再加');
      // 通过this.$store.dispatch调用actions里的方法
      this.$store.dispatch('jiaOdd',this.n)
    },
  }
}
</script>
  1. 关于辅助函数

vuex使用的另一种更简便的用法,可以借助辅助函数:mapState, mapGetters, mapActions, mapMutations

<template>
  <div>
    <h3>vuex学习:求和案例_mapActions和mapMutations</h3>
    <p>当前求和:{{ sum }}</p>
    <p>当前求和方法10倍:{{ $store.getters.bigSum }}</p>
    <p>我在{{ school }},学习{{ subject }}</p> 
    <select v-model.number="n">
      <option value="1">1</option>
      <option value="2">2</option>
      <option value="3">3</option>
    </select>
    <button @click="increment(n)">+</button>
    <button @click="decrement(n)">-</button>
    <button @click="incrementOdd(n)">当前求和为奇数再加</button>
    <button @click="incrementWait(n)">等一等再加</button>
  </div>
</template>

<script>
import { mapState, mapGetters, mapActions, mapMutations } from "vuex";
export default {
  data() {
    return {
      n: 1, //用户选择数字
    };
  },
  computed: {
    // sum(){
    //   return this.$store.state.sum
    // },
    // school(){
    //   return this.$store.state.school
    // },
    // subject(){
    //   return this.$store.state.subject
    // },

    // 借助mapState生成计算属性,从state中读取数据.
    // 对象写法(计算属性值和state的值不一致):
    // ...mapState({a:'sum',haha:'school',na:'subject'})
    // 数组写法(计算属性值和state的值必须保持一致):
    ...mapState(["sum", "school", "subject"]),

    // 借助mapGetters生成计算属性,从getter中读取数据.
    //  对象写法:
    // ...mapGetters({ bigSum: "bigSum" }),
    ...mapGetters(["bigSum"]),
  },
  methods: {
    // increment() {
    //   // 通过this.$store.commit调用mutations里的方法
    //   this.$store.commit("JIA", this.n);
    // },
    // decrement() {
    //   this.$store.commit("JIAN", this.n);
    // },

    //借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
    // 参数通过调用方法的时候传进来
    ...mapMutations({increment:'JIA',decrement:'JIAN'}),
    // (数组写法)
    // ...mapMutations(['JIA','JIAN']),

    // incrementOdd() {
    //   console.log("当前求和为奇数再加");
    //   // 通过this.$store.dispatch调用actions里的方法
    //   this.$store.dispatch("jiaOdd", this.n);
    // },
    // incrementWait() {
    //   console.log("等一等再加");
    //   this.$store.dispatch("jiaWait", this.n);
    // },
    

    //借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)
    ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})

     //(数组写法)
			// ...mapActions(['jiaOdd','jiaWait'])

  },
  created() {
    console.log("mapState=", mapState);
  },
};
</script>

<style>
</style>

四、Vuex的应用场景/什么时候该用Vuex

  1. 多个组件依赖统一数据或状态时;
  2. 来自不同组件的行为需要变更同一状态时;

五、Vuex的持久性储存

Vuex的数据,会有一个问题:页面或浏览器刷新时,数据会丢失

解决方案:借助插件vuex-persistedstate对Vuex的数据进行持久性储存

补充:在Vue3中不推荐使用Vuex,官方推荐Pinia

因为Vuex在 API 的设计上,对TS 的类型推导的支持比较复杂,在TS会用起来很痛苦。为了解决这个问题,Vuex的作者发布了Pinia,并称为下一代Vuex。


六、Vue思考题

1、Vuex的actions和mutations有什么区别?

  1. actions主要用于响应组件中的动作,通过 commit( )来触发 mutation 中函数的调用, 间接更新 state,不是必须存在的,;

mutations主要用于直接操作数据,修改数据,是必须存在的;

  1. actions可以进行异步操作,可用于向后台提交数据或者接受后台的数据;

mutations中是同步操作不能写异步代码、只能单纯的操作 state ,用于将数据信息写在全局数据状态中缓存,不能异步操作;