【Vue】Vuex

Vue 状态管理工具 — Vuex

1.简介

Vuex 是一个专门为 Vue.js 应用程序开发的状态管理模式,它采用集中式存储管理应用的所有组件状态,并以相应的规则保证状态以一种可预测的方式发生变化。可以理解为:将多个组件共享的变量全部存储在一个对象里面,然后将这个对象放在顶层的 Vue 实例中,让其他组件可以使用,它最大的特点是响应式

状态 — 实际上就是共享的数据,有时也称为状态数据,例如:A、B两个组件之间共用的某个对象n。

所以,一般情况下,我们会在 Vuex 中存放一些需要在多个界面中进行共享的信息。比如用户的登录状态、用户名称、头像、地理位置信息、商品的收藏、购物车中的物品等,这些状态信息,我们可以放在统一的地方,对它进行保存和管理。

2.搭建 Vuex 环境

2.1 安装

npm install --save vuex@版本号

这里注意,vue 的 2.x 版本对应 vuex 的 3.x 版本,vue 的 3.x 版本对应 vuex 的 4.x 版本

2.2 配置

配置 Vuex 的第一步是创建store

一般来说,实际开发中的标准方法是,在 src 中创建一个 store 文件夹,并在其中保存一个 index.js 来创建store,然后再直接应用。

Vue2 创建方式
  1. 创建store

    // index.js
    // 首先引入Vuex并使用插件
    import Vue from 'vue
    import Vuex from 'vuex'
    
    
    //1.安装插件,不然会报错(不允许在使用插件之前创建store!)
    Vue.use(Vuex)
     
    //2.创建对象
    const store = new Vuex.Store({
      state:{
        // ...
      },
      mutations:{
        // ...
      },
      actions:{
        // ...
      },
      getters:{
        // ...
      },
      modules:{
        // ...
      }
    })
    //3.导出使用
    export default store
    
  2. 应用store

    // 在入口文件 main.js 中创建Vue实例时,配置 store 选项
    import store from './store/index.js'
    ...
    new Vue({
        el: '#app',
        render: h => h(App),
        store: store,
    })
    

完成这两步后,此时$store属性就会出现在Vue实例对象以及组件实例对象身上了!

后续,要使用store时,直接用this.$store即可获取到!

Vue3 创建方式

与 Vue2 不同,在 Vue3 中不再使用Vuex.Store({...})这一API,而是引入了新的方法createStore({...})来创建store

同时,也不再需要在创建store前应用插件了!

在创建store之前,只需要在 main.js 中使用一下插件即可:app.use(vuex)

  1. 创建store

    // index.js
    import { createStore } from 'vuex'
    
    export default createStore("xxx", {
      state:{
        // ...
      },
      mutations:{
        // ...
      },
      actions:{
        // ...
      },
      getters:{
        // ...
      },
      modules:{
        // ...
      }
    })
    
  2. 使用store

    在 Vue3 中,由于在 setup 函数中无法获取this指针,所以不再通过this.$store来使用store,而是在需要使用store的模块中,引入 src/store/index.js 中暴露出来的useStore方法!

    
    <template>
    	...
    template>
    
    <script setup>
    import { useStore } from '@/store/index.js'
    
    ...
    const store = useStore();
        
    ...
    script>
    

    即,通过一个变量接收useStore()方法返回的store实例,并通过这个实例来访问、修改store

3.Vuex的核心概念

vuex中一共有五个状态 State Getter Mutation Action Module

下面是官方给出的 Vuex 状态管理图例:
【Vue】Vuex_第1张图片

3.1 State

提供唯一的公共数据源,所有共享的数据统一放到store的state进行储存

在vuex中state中定义的数据,可以在任何组件中进行调用!

需要注意的是,读取 Vuex 中的数据时,可以直接通过this.$store.state.xxxstore.state.xxx来获取。但是要修改其中的数据时,需要用dispatchcommit方法!

以 Vue2 中的使用为例:

this.$store.dispatch('actions中的某一方法名', 数据)
this.$store.commit('mutations中的某一方法名', 数据)

那么什么时候使用dispatch方法,什么时候又要使用commit方法呢?

实际上,从上面的使用方式中,就可以看出这两者的区别,即:dispatch方法调用的是actions中的方法,而commit方法调用的则是mutation中的方法!

那么这两者的区别又在哪?关于这一点后面会进行介绍,先往下看。

3.2 Mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的事件类型 (type)和一个回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数/

定义 Mutation 中的方法时:

参数state是必要的,同时也可以直接传递一个参数,例如:

mutations: {
	add(state, num) {
		state.count = state.count + num
	},
}

而要使用 Mutation 中的方法,就需要用commit方法触发:

this.$store.commit("add", 5);	// Vue2
store.commit("add", 5);		    // Vue3

3.3 Action

Action 和 Mutation 相似,一般不用 Mutation 进行异步操作,若要进行异步操作,使用 Action

这里就可以回答上面那个问题,Action 与 Mutation 的区别在于:Action 中往往用于进行一些异步操作,例如定时器、向后端接口请求数据等等。Mutation 则往往用于执行一些同步操作。而这样设计是为了方便 devtools 跟踪数据变化,从而方便管理。

因此上面所说仅仅是规范,而不是逻辑上的不允许!

所以,对于上面的问题,即使用dispatchcommit方法的时机,可以总结为:若没有网络请求或其它异步业务逻辑,组件中也可以越过 Action,直接调用 Mutations 中的方法,即直接使用commit方法!

而至于 Action 的使用,实际上与 Mutation 是相同的。

首先定义 Action 中的方法:

Action 中的方法接收一个必要参数context,即上下文,实际上就是store对象!同样的,其也可以接收一些自定义的参数。

actions: {
	asyncAdd(context,num) {
		setTimeOut(() => {
            context.commit("add", num)	// 通过Mutation中的方法改变state
        }, 1000);
	},
}

然后通过dispatch方法触发:

this.$store.commit("asyncAdd", 5);	// Vue2
store.commit("asyncAdd", 5);	    // Vue3

实际上,这里可以发现,在 Action 中处理异步操作时,其内部仅仅是处理异步逻辑,而真正改变state还是通过 Mutation 中的方法

3.4 Getter

当 state 中的数据需要经过加工后再使用时,可以使用 Getter 进行加工!

Getter 实际上与计算属性 computed 十分相似,它们都是用于对数据进行加工后再使用,它们也同样会进行缓存!

例:

getters: {
	powerCount(state) {
        return state.counter * state.counter;
    },
    ...
}

可以看到 Getter 中的方法,同样接收一个state作为必要参数,但其不需要传入其他参数。

使用时,直接获取即可:

this.$store.getters.powerCount  // Vue2
store.getters.powerCount        // Vue3

但是如果真的需要传入参数,该怎么办呢?

此时可以在对应方法中,返回一个函数,例如:

moreAgeStu(state){
   return function(age){
     return state.students.filter(s => s.age > age)
   }
}

这样,在调用时传入参数即可:

this.$store.getters.moreAgeStu(18)  // Vue2
store.getters.moreAgeStu(18)        // Vue3

4.Vuex的4个map方法 — 优化程序

4.1 mapState() — 映射state中的数据为计算属性

借助mapState()方法,我们可以根据state生成计算属性,从state中读取数据,实际上是由mapState()方法生成了多个对应的函数返回state中指定属性的值!

如何使用?

有两种方法:

  1. 对象写法:

    import { mapState } from 'vuex'
    ...
    
    computed({
    	// 使用ES6的扩展运算符将生成的计算属性展开在computed中
        // 对象写法可以指明生成的计算属性的名称
    	...mapState({sum: 'sum', school: 'school', ...})
    })
    
  2. 数组写法:

    import { mapState } from 'vuex'
    ...
    
    computed({
    	// 仅指定需要读取的state中属性的名称,并将这个名称作为生成计算属性的名称!
    	...mapState(['sum', 'school', ...])
    })
    

4.2 mapGetters() — 映射getters中的数据为计算属性

借助mapGetters()方法,我们可以根据getters生成计算属性,从getters中读取数据,实际上是由mapGetters()方法生成了多个对应的函数返回getters中指定属性的值!

同样有两种写法:

  1. 对象写法:

    import { mapGetters } from 'vuex'
    ...
    
    computed({
    	// 使用ES6的扩展运算符将生成的计算属性展开在computed中
        // 对象写法可以指明生成的计算属性的名称
    	...mapGetters({bigSum: 'bigSum', ...})
    })
    
  2. 数组写法:

    import { mapState } from 'vuex'
    ...
    
    computed({
    	// 仅指定需要读取的getters中属性的名称,并将这个名称作为生成计算属性的名称!
    	...mapGetters(['bigSum', ...])
    })
    

4.3 mapActions() — 生成与actions对话的方法

借助mapActions()方法,我们可以生成与 actions 对话的方法,即其中包含$store.dispatch('xxx',value)的方法!

在 Vue2 中的使用如下:

import { mapActions } from 'vuex'
...
method: {
	...mapActions(['ADD', 'SUB'])	
}

4.4 mapMutations() — 生成与mutations对话的方法

借助mapMutations()方法,我们可以生成与 mutations对话的方法,即其中包含$store.commit('xxx',value)的方法!

在 Vue2 中的使用如下:

import { mapMutations } from 'vuex'
...
method: {
	...mapMutations(['ADD', 'SUB'])	
}

4.5 在Vue3中的使用

由于 Vue3 中,不再使用data、method、computed等模块,所以导致上述4个方法在 Vue3 中的映射效果不如人意。那么,我们如何才能在 Vue3 中使用这些API呢?

答案是封装这4个方法

详情参见:封装vuex的4个map方法

5.Vuex模块化 + 命名空间

当遇见大型项目时,数据量大,store就会显得很臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块。

而为了模块拥有更高的封装度和复用性,我们还可以配合命名空间配置项namespace一起使用。即,在模块中开启 namespace 配置:

xxxModule: {
	namespace: true,
    state: {
    	...
    },
    ...
}

从而使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。

所以,使用时需要指定模块名

你可能感兴趣的:(Vue,vue.js,javascript)