什么是vuex

vuex官方是这么介绍的

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

vuex是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间数据的共享。

使用vuex的好处

1.能够在vuex中集中管理共享的数据,易于开发和后期维护。

2.能够高效地实现组件之间的数据共享,提高开发效率。

3.vuex中存储的数据都是响应式的,能够实时保持数据与页面同步。

使用vuex的场景

一般来说,只有组件之间共享的数据才有必要存储到vuex中,组件的私有数据依旧存储在组件自身的data中即可。

基本使用

1
2
3
4
5
const store =new Vuex.Store({
//state中存放的就是全局共享的数据
state:{count:0}
})

Vuex的核心概念

vue官网图

vuex的核心概念:state、mutations、actions、getters、modules。上图是官网上的流程图,我们来分析一下每一环的作用

State

单一状态树,state提供唯一的公共数据源,提供唯一公共数据

1
2
3
4
//创建store数据源,提供唯一公共数据
const store = new Vuex.Store({
state:{ count:0 }
})

组件访问state中数据的第一种方式

1
this.$store.state.全局数据名称

例如

1
2
<p>当前count值为:{{ $store.state.count }}</p>
//在template中this可以忽略

组件访问state中数据的第二种方式

1
2
//1.在组件中从vuex按需导入mapstate函数
import { mapstate } from 'vuex'

通过刚才导入的mapstate函数,将当前组件需要的全局数据,映射为当前组件的computed计算属性

1
2
3
4
5
//2.将全局数据映射为当前组件的计算属性
computed:{
...mapstate(['count'])
// ...为展开运算符
}

在组件中即可直接使用count

1
<p>当前count值为:{{ count }}</p>

Mutations

组件修改state中的数据

错误操作

我们想要实现点击按钮改变count的数值的话,可能会想给按钮添加一个@click事件让他数值+1

1
2
3
4
5
6
7
8
数据<p>当前count值为:{{ $store.state.count }}</p>
<button @click="countAdd"> +1 </button>

methods: {
countAdd(){
this.$store.state.count++
}
}

但是注意:mutations在处理异步操作时,能够引起页面的响应式变化,但是 devtools 无法进行监听。

正确操作

正确的操作应该是使用Mutation

在vuex中只能通过mutation变更Store数据,不可以直接操作Store中的数据。

通过这种方式虽然操作起来繁琐了一些,但是可以集中监控所有数据的变化。不然到项目后期根本不知道是在哪个地方修改了数据,不利于维护。

定义mutation

1
2
3
4
5
6
7
8
9
10
11
export default new Vuex.Store({
state: {
count:0
},
mutations: {
add(state){
//变更状态
state.count++
}
}
})

触发mutation

1
2
3
4
5
6
7
//触发mutation
methods:{
countAdd(){
//触发mutations的第一种方式
this.$store.commit('add')
}
}

commit的作用就是调用某个mutation函数

触发mutation时传递参数

如果我们不是让数值+1,而是加任意值该怎么做呢?可以在触发mutations时传递参数

1
2
3
4
5
6
7
8
9
10
11
12
export default new Vuex.Store({
state: {
count:0
},
mutations: {
//在这里加上形参
addN(state,step){
//变更状态
state.count+= step
}
}
})

触发携带参数的mutation

1
2
3
4
5
6
methods:{
countAdd(){
//触发mutations时携带参数
this.$store.commit('addN',3)
}
}
注意:在大多数情况下,传递的参数(载荷)应该是一个对象,这样可以包含多个字段并且记录的mutation会更易读

触发mutations的第二种方式

上面this.$store.commit()是出发mutations的第一种方式,下面介绍一下第二种

1
2
//从vuex中按需导入mapMutations函数
import { mapMutations } from 'vuex'

通过导入的mapMutations函数,将需要的mutations函数映射为当前组件的methods方法

1
2
3
4
5
6
7
8
methods:{
...mapMutations(['add','addN']),
countAdd(){
//可以直接调用add或者addN
this.add()
this.addN(5)
}
}

Action

如果通过异步操作变更数据,建议(必须)通过Action,而不能使用Mutation。但是在Action中还是要通过触发Mutation的方式间接变更数据。

不要在mutations中执行异步操作,比如想点击按钮一秒后再让数据变化,在mutation中写定时器这种操作是有问题的
我们的解决方案是用Action,它是专门用来处理异步任务的。这样 devtools 就能够进行跟踪,由 Actions 处理异步操作,具体的函数部分仍交由 Mutations 进行处理。

同步任务,异步任务

定义Action

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
export default new Vuex.Store({
//省略其他代码
mutations: {
add(state){
state.count++
}
},
actions:{
//context:上下文,默认写法
addAsync(context){
//这里只负责执行setTimeout延迟操作
setTimeout(()=>{
//修改数据的操作提交给mutation
context.commit('add')
},1000)
}
}
})

触发Action

1
2
3
4
5
6
7
methods:{
countAdd(){
//触发actions的第一种方式
this.$store.dispatch('addAsync')
//dispatch函数专门用来触发action
}
}

触发携带参数的Action

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//定义Action
export default new Vuex.Store({
//省略其他代码
mutations: {
addN(state,step){
state.count += step
}
},
actions:{
//传入形参
addAsync(context,step){
setTimeout(()=>{
//实参
context.commit('addN',step)
},1000)
}
}
})
1
2
3
4
5
6
7
//触发Action
methods:{
countAdd(){
//调用dispatch,携带参数
this.$store.dispatch('addNAsync',5)
}
}

触发mutations的第二种方式

1
2
//从vuex中按需导入mapActions函数
import { mapActions } from 'vuex'

通过导入的mapActions函数,将需要的Actions函数映射为当前组件的methods方法

1
2
3
4
5
6
7
8
9
methods:{
...mapActions(['add','addN']),//需要用哪个就定义哪个
countAdd(){

}
}
{% label 注意:
//...mapActions(['add','addN']) 这里的add和addN已经被映射为当前组件的方法,可以直接绑定在click事件上
如:<button @click="addN(5)">+5 acync</button> green %}

Getter

Getter用于对Store中的数据进行加工处理形成新的数据,不会修改Store中的原数据。类似于计算属性,在数据展示前进行一些变化处理,具有缓存功能,能够提高运行效率。

定义Getter

1
2
3
4
5
6
7
8
9
10
export default new Vuex.Store({
state: {
count:0
},
getters: {
showNum: state =>{
return '当前最新的数量是【'+ state.count +'】'
}
}
})

使用getters的第一种方式

1
2
3
this.$store.getters.名称
<p> {{ $store.getter.showNum }} </p>
//template中this可以省略

使用getters的第二种方式

1
2
3
4
5
import { mapGetters } from 'vuex'

computed:{
...mapGetters({'showNum'})
}

到这里vuex中的核心我们已经都了解了,那我们再总结一下

总结

module

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store对象就有可能变得相当臃肿。

所以,vuex允许我们将state分割成模块(module),每个模块拥有自己的state、mutation、action、getter甚至是嵌套子模块—-从上至下进行同样方式的分割。

module

在项目实际使用中,我们一般在store文件夹中创建一个modules文件夹,把每个modules单独写为一个js文件,然后在index.js中import引入这些js文件即可。

模块化写法

模块化的vuex

vuex模块化

辅助函数

MapStates

MapMutations

MapActions

MapGetters

完结撒花🎉