Vuex

Vuex

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

1699451517587

安装

安装时注意版本,一般情况下,Vue2使用Vuex3,Vue3使用Vuex4,否则或出现报错情况

1
npm install vuex@next --save

核心概念

State

State,作为数据源,负责保存数据

1
2
3
4
5
//存储数据
const state = {
sum: 0,
num: 1
}

每当state中的数据发生变化时,就会重新进行属性的计算,触发更新关联的DOM元素,在mian.js里面引入store,就会通过$store.state访问到组件所需要的变量。

1
2
3
4
5
6
7
8
export default {
name: 'App',
computed:{
sum(){
return this.$store.state.sum
}
}
}

由于此时使用state中的数据不方便,代码不简洁,所以Vue官方引入了mapState辅助函数,方便进行state中数据进行读取

使用对象展开运算符一共有两种写法,一种使用对象写法,一种使用数组写法,对象写法更具有可读性

1
2
3
4
5
6
7
8
9
10
11
computed:{
sum(){
return this.$store.state.sum
},
//从state里面取数据,对象写法
// ...mapState({sum:'sum'})

//数组写法
...mapState(['sum'])

},

Getter

抛砖引玉,假若上一个sum共享变量需要进行×10倍之后,如果在组件中可以使用计算属性进行解决,或者使用插值表达式,但是不利于代码的复用,所以构建出了Getter,可以对State变量进行操作,然后在组件中进行读取。

getter可以接收一个参数,入参代表的是State,经过修改之后,可以直接在Vue组件中进行使用

1
2
3
4
5
6
//加工state属性
const getters = {
getSun(state){
return state.sum *10
}
}

可以直接在组件里进行使用,$store.getter可以访问到加工之后的属性值,但是如上所示,此时,比较代码重复度比较高,所以官方推出了一个mapGetters辅助函数,用于简化开发,同样也可以使用数组形式

1
2
3
4
...mapGetters({
// 把 `this.doneCount` 映射为 `this.$store.getters.doneTodosCount`
doneCount: 'doneTodosCount'
})

Multation

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

1
2
3
4
5
6
7
//操作数据
const mutations = {
SUB(state,参数一,参数二...){
state.sum -= 1
console.log(state.sum)
}
}

注意:不可以直接调用multation函数,只可以通过store里的commit函数进行调用

1
store.commit('SUB',参数一,参数二);

同样,在组件中也会有mapMultations,作为辅助函数,减少代码量的书写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { mapMutations } from 'vuex'

export default {
// ...
methods: {
...mapMutations([
'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`

// `mapMutations` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
]),
...mapMutations({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
})
}
}

Action

Action 类似于 mutation,不同在于:

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

Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters

1
2
3
4
5
6
//响应组件动作
const actions = {
sub(context, b) {
context.commit('SUB')
}
}

充分实现了组件与vuex之间的解耦合,使得程序变得更加清晰

分发Action,通过store.dispatch触发Action动作,进而调用Multation

1
store.dispatch('sub')

multation中异步操作受到限制,但在Action内部可以实现异步操作

1
2
3
4
5
6
7
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('sub')
}, 1000)
}
}

在组件中同样会有一个辅助函数,mapActions,将methods映射为store.dispatch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { mapActions } from 'vuex'

export default {
// ...
methods: {
...mapActions([
'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`

// `mapActions` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
]),
...mapActions({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
})
}
}

项目结构树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
├── index.html
├── main.js
├── api
│ └── ... # 抽取出API请求
├── components
│ ├── App.vue
│ └── ...
└── store
├── index.js # 我们组装模块并导出 store 的地方
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation
└── modules
├── cart.js # 购物车模块
└── products.js # 产品模块

模块化使用

创建一个store文件夹存放一个index.js,保存store模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import Vuex from "vuex";
import Vue from "vue";

Vue.use(Vuex)

//响应组件动作
const actions = {
sub(context, b) {
context.state.sum += 1
// context.commit('SUB')
}
}

//操作数据
const mutations = {
SUB(state){
state.sum -= 1
console.log(state.sum)
}
}

//存储数据
const state = {
sum: 0
}

//加工state属性
const getters = {
getSun(state){
return state.sum *10
}
}

//创建store对象,并且导出
export default new Vuex.Store({
actions,
mutations,
state,
getters
})

在main.js中将store文件进行引入

1
2
3
4
5
6
7
8
9
10
11
import Vue from 'vue'
import App from './App.vue'
import store from "@/store"

Vue.config.productionTip = false


new Vue({
render: h => h(App),
store
}).$mount('#app')