目录

🌩 Vuex 状态管理

# Vuex 的理解

  • Vuex 的概念: 专门在 Vue 中实现集中式状态(数据)管理的一个 Vue 插件,对 Vue 应用中的多个组件的共享状态进行集中式的管理(读 / 写操作),也是组件之间通信的一种方式,并且适用于任意组件间的通信。

🔗 相关链接:


  • 使用全局事件总线实现多组件共享数据:不只是读、还要写的操作时,就显得有点繁杂。
image-20220407112423289
  • 使用 Vuex 多组件共享数据:读写可以在双向操作。

image-20220407112832380

  • 什么时候使用 Vuex :共享一个状态。
    • 多个组件依赖与同一个状态。
    • 来自不同的组件的行为需要变更同一个状态。

# Vuex 原理

vuex

  • Vuex 三个重要组成部分:

    • Actions:动作行为。
    • Mutations:加工维护。
    • State:状态(数据) ,数据交给 State 保管,可以包含许多数据关键词。
  • 可以在图中了解 Vuex 的工作流程。

    • 三个组成部分可以看成是三个对象中存储数据。

    • Actions 与后端接口相关,当组件进行的动作的数据需要通过 AJAX 请求获取,即不知道动作的数据。实际上,当组件知道进行的动作和数据,Vue 会直接 Commit 到 Mutations,即 Actions 可以省略。

    • Mutations 是实际加工状态的地方。

    • Vuex 基于 store 工作。例如, store.dispatchstore.commitstore.mutate ,在任意组件中都能调用。

# Vuex 的使用

# Vuex 的开发环境

  • 安装 Vuex 3:
$ npm i vuex@3
1

提示

在 2022 年 2 月 7 日,Vue 3 成为了默认的版本(即执行 npm i vue 安装的版本是 Vue 3),Vuex 也更新到了 Vuex 4 版本,而 Vue 4 只能在 Vue 3 中运行(所以执行 npm i vuex 安装的是 Vuex 4)。所以,在目前基于 Vue 2 版本的学习,我们要安装可以基于 Vue 2 运行的 Vuex 3。

  • 创建 Vuex 的操作文件 src/store/index.js
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)

//准备actions对象——响应组件中用户的动作
const actions = {}
//准备mutations对象——修改state中的数据
const mutations = {}
//准备state对象——保存具体的数据
const state = {}

//创建并暴露store
export default new Vuex.Store({
	actions,
	mutations,
	state
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

提示

注意:由于 store 的引入不能晚于引入插件 Vuex ,所以现在操作文件中引入 Vue 并且引入插件 Vuex ,然后在入口文件就直接引入 store 配置项即可。

  • 在入口文件 main.js 中创建 vm 实例时传入 store 配置项:
......
//引入store
import store from './store'
......

//创建vm
new Vue({
	el:'#app',
	render: h => h(App),
	store
})
1
2
3
4
5
6
7
8
9
10
11

# Vuex 的基本使用

  • store 操作文件 index.js 中初始化数据,配置 actionsmutations 的方法:
// 响应组件中的动作
const actions = {
	add(context,value){
		context.commit('ADD',value)
	},
}

// 执行动作的实际操作
const mutations = {
	ADD(state,value){
		state.sum += value
	}
}

//初始化数据
const state = {
   sum:0
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

提示

  • actions 中定义的方法有两个参数:

    • context :上下文相关的内容,一般提供给动作中的方法中所用到的内容。(可以看作是 store 的部分内容)

      注意:包括 dispatch :可以用来在方法中调取在 actions 中的其他方法。实际开发中没有像例子那么简单的业务逻辑,甚至如此简单的业务逻辑可以在组件中直接完成,但是一旦有繁杂的业务逻辑此时 actions 的作用就显现出来,并且可以在 actions 中定义多个方法包含多种业务逻辑提供其中一个方法使用 dispatch 调取。

    • value :组件传递给 Vuex 要进行操作的数据。在 actions 中可对此进行判断等业务逻辑。

  • mutations 中定义的方法有两个参数:

    • state :提供用来读取 state 中的数据(状态)。
    • value :组件传递给 Vuex 要进行操作的数据。
  • mutations 中的方法名命名一般为大写actions 中的方法进行区分。并且在 mutations 中一般直接操作数据,而不再过多进行判断等业务逻辑(一般放在 actions 中进行)。

  • 在组件中使用 Vuex :

    • 组件中读取 Vuex 中的数据$store.state.sum (在模版中)

    • 组件中操作 Vuex 中的数据:通过 dispatch$store.dispatch('method', value) 或者 commit$store.commit(‘method’, value)

      注意:若在该操作没有网络请求或者其他业务逻辑,组件可以越过 actionsdispatch ,直接使用 mutationscommit

# getters 的使用

  • 概念:当在 state 中的数据需要经过(复杂)加工之后使用,可以使用 getters 加工。(在组件中的计算属性 computed 只能提供在目前组件中使用,不能共享给其他组件复用)

  • store 操作文件 index.js 中追加配置 getters ,注意要追加到暴露项中:

const getters = {
	bigSum(state){
		return state.sum * 10
	}
}

//创建并暴露store
export default new Vuex.Store({
	......
	getters
})
1
2
3
4
5
6
7
8
9
10
11
  • 在组件中读取加工后的数据: $store.getters.bigSum

# 四个 map 方法的使用

当初始化的数据较多,在组件中的模版语法使用过长的代码语句获取在 Vuex 的数据不太好,所以可以使用计算属性 computed 获取。

🌰 例子:使用计算属性获取 Vuex 中的数据:

点击查看
computed: {
  sum() {
    return this.$store.state.sum
  },
  school() {
    return this.$store.state.school
  },
  subject() {
    return this.$store.state.subject
  },
  bigSum() {
    return this.$store.getters.bigSum
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

可以看出在计算属性中有相同部分的代码,都是从 state 或者 getter 中获取数据,所以 Vuex 提供了生成 state 或者 getters 计算属性的方法 mapStatemapGetters

  • mapState 方法:可以映射 state 的数据为计算属性:
computed: {
    //借助mapState生成计算属性:sum、school、subject(对象写法)
     ...mapState({sum:'sum',school:'school',subject:'subject'}),
         
    //借助mapState生成计算属性:sum、school、subject(数组写法)
    ...mapState(['sum','school','subject']),
},
1
2
3
4
5
6
7
  • mapGetters 方法:可以映射 getters 的数据为计算属性:
computed: {
    //借助mapGetters生成计算属性:bigSum(对象写法)
    ...mapGetters({bigSum:'bigSum'}),

    //借助mapGetters生成计算属性:bigSum(数组写法)
    ...mapGetters(['bigSum'])
},
1
2
3
4
5
6
7

🌰 使用原始方法与 Vuex 中的 actions 对话:

点击查看
methods: {
  increment() {
    this.$store.commit('ADD', this.n)
  },
  decrement() {
    this.$store.commit('SUB', this.n)
  },
  incrementOdd() {
    this.$store.dispatch('addOdd', this.n)
  },
  incrementWait() {
    this.$store.dispatch('addWait', this.n)
  }
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14

与上面两个 map 方法类似,Vuex 提供了帮助生成 actionsmutations 对话的方法 mapActionsmapMutations

  • mapActions 方法:用于帮助生成与 actions 对话的方法,包含 $store.dispatch(xxx) 的函数:
methods:{
  //靠mapActions生成:incrementOdd、incrementWait(对象形式) 
  ...mapActions({incrementOdd: 'addOdd', incrementWait: 'addWait'}),
    
  //靠mapActions生成:incrementOdd、incrementWait(数组形式)
  ...mapActions(['addOdd', 'addWait'])
}
1
2
3
4
5
6
7
  • mapMutations :用于帮助生成与 mutations 对话的方法,包含 $store.commit(xxx) 的函数:
methods:{
  //靠mapActions生成:increment、decrement(对象形式)
  ...mapMutations({increment: 'ADD', decrement: 'SUB'}), 
   
  //靠mapMutations生成:JIA、JIAN(对象形式)
  ...mapMutations(['ADD','SUB'])
}
1
2
3
4
5
6
7

注意

使用数组法生成,要求事件名与在 actions 或者 mutations 中的方法名称相同,即在模板中要使用在 actions 或者 mutations 中的方法名称。

注意

此时 mapActionsmapMutations 帮我们生成的对象方法相当于:

increment(value) {
  this.$store.commit('ADD', value)
}
1
2
3

说明要传递参数到 actions 或者 mutations 中的方法中,需要在模版中绑定事件时就传递好参数,否则参数是事件对象,例如: <button @click="increment(n)">+</button>

提示

真实依靠计算属性获取与使用 map 获取在 Vue 开发者工具中的区别:

image-20220407162402333

# Vuex 的使用案例

# 🌰 使用 Vuex 实现求和案例

store 操作文件 index.js 中:

  • 初始化数据,准备用于操作的数据 state
const state = {
    sum: 0 // 当前求和的结果
}
1
2
3
  • 准备用于响应组件中的动作 actions
const actions = {
    add(context, value) {
        context.commit('ADD', value)
    },
    sub(context, value) {
        context.commit('SUB', value)
    },
    addOdd(context, value) {
        if (context.state.sum % 2) {
            context.commit('ADD', value) 
        }
    },
    addWait(context, value) {
        setTimeout(() => {
            context.commit('ADD', value) 
        }, 500)
    },
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  • 准备操作(加工)数据(状态)的 mutations
const mutations = {
    ADD(state, value) {
        state.sum += value
    },
    SUB(state, value) {
        state.sum -= value
    }
}
1
2
3
4
5
6
7
8

在组件中使用 Vuex :

  • 读取 Vuex 中的数据:
<h1>当前的求和为: {{$store.state.sum}}</h1>
1
  • 操作 Vuex 中的数据:
methods: {
  increment() {
    this.$store.dispatch('add', this.n)
  },
  decrement() {
    this.$store.dispatch('sub', this.n)
  },
  incrementOdd() {
    this.$store.dispatch('addOdd', this.n)
  },
  incrementWait() {
    this.$store.dispatch('addWait', this.n)
  }
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14

注: n 为定义用来选择递增的数字。

提示

对上述改进,可以看出在 actions 中的 addsub 方法并没有其他的业务逻辑,所以在组件中可以直接使用 commit 直接越过 actioins

methods: {
  increment() {
      this.$store.commit('ADD', this.n)
  },
  decrement() {
    this.$store.commit('SUB', this.n)
  }
}
1
2
3
4
5
6
7
8

# 🌰 多组件共享数据

除了第一个例子的 Count 组件以外,新加一个组件 Person ,使用 Vuex 共享两个组件都可以使用的数据。

详细步骤不赘述,其实不同组件获取 Vuex 中的数据都可可以使用普通方法或者 map 方法,从而实现共享数据。

# Vuex 的模块化编码

  • 当多个组件同时使用 Vuex 实现共享数据并且操作数据时,此时的 store 文件其实「不堪重负」,在 actionsmutations 中的方法繁杂起来,不同组件操作不同的方法,造成编码混乱和版本管理困难。

  • 使用模块化编码的目的:让代码更好维护,让多种数据分类更加明确

🌰 将上述的 Vuex 例子进行模块化编码,

  • 修改 Vuex store.js ,分割为如下文件:

    • countOptions.js

    • personOptions.js

export default {
  namespaced:true,//开启命名空间
  state:{ ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}
1
2
3
4
5
6
7

注意:开启命名空间才能在组件中使用如 countOptions 的命名。

然后在 store.js 暴露时,使用 modules 包含以上的模块:

export default new Vuex.Store({
    modules: {
        countOptions,
        personOptions
    }
})
1
2
3
4
5
6
  • 在组件中,使用模块化编码后:

读取 state 数据:

//方式一:自己直接读取
this.$store.state.personOptions.personlist

//方式二:借助mapState读取:
...mapState('countOptions',['sum','school','subject']),
1
2
3
4
5

读取 getters 数据:

//方式一:自己直接读取
this.$store.getters['personOptions/firstPersonName']

//方式二:借助mapGetters读取:
...mapGetters('countOptions',['bigSum'])
1
2
3
4
5

注意: $store.gettersstate 不同。

调用 dispatch

//方式一:自己直接dispatch
this.$store.dispatch('personAbout/addPersonWang',person)

//方式二:借助mapActions:
...mapActions('countOptions',{incrementOdd:'addOdd',incrementWait:'addWait'})
1
2
3
4
5

调用 commit

//方式一:自己直接commit
this.$store.commit('personOptions/ADD_PERSON',person)

//方式二:借助mapMutations:
...mapMutations('countOptions',{increment:'ADD',decrement:'SUB'}),
1
2
3
4
5
📢 上次更新: 2022/09/02, 10:18:16