Vue探索之-Vuex状态管理模式

前言

关于Vuex,官网是这么给的定义:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。本篇将通过一个小Demo来说一下Vuex的用处,一起来看看吧!


点我查看官网文档

为什么会有Vuex

Vuex是为了存放组件之间的共享数据而诞生的,组件中的共享数据可以直接挂载到Vuex中,以供APP中所有的组件使用,而不必通过父子组件之间传值了,这就有效解决了非父子组件之间的传值问题,对于非共享数据是没必要放在Vuex中的,组件内部的私有数据只放在组件各自的data中即可。总言之,Vuex就是一个全局的共享型数据存储区域,可以看做一个数据仓库。

需求

有这么一个需求,及其对应初始代码块,如下图
Loding...
Loding...

看下代码

App.vue

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
<template>
<div class="box">
<p class="input">
<input type="text" placeholder="0">
</p>
<counter></counter>
<amount></amount>
</div>
</template>

<script>
import counter from './components/counter'
import amount from './components/amount'

export default {
data(){
return{
}
},
components:{
counter,
amount
}
}
</script>

<style lang="scss" scoped>
.box{
width: 300px;
height: 120px;
background-color: #007acc;
padding: 10px;
.input{
text-align: center;
}
}
</style>

counter.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div class="mt">
<button>-</button>
<button>+</button>
</div>
</template>

<script>
export default {

}
</script>

<style lang="scss" scoped>
.mt{
display: flex;
justify-content: space-around;
button{
width: 30px;
height: 20px;
}
}
</style>

amount.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div class="amount">
<h3>当前的值为</h3>
</div>
</template>

<script>
export default {

}
</script>

<style lang="scss" scoped>
.amount{
h3{
text-align: center;
margin: 0;
}
}
</style>

安装Vuex

  1. 下包:npm install vuex --save
  2. 在main.js中做全局配置:

    1
    2
    import Vuex from 'vuex'
    Vue.use(Vuex)
  3. new一个store实例:

    1
    2
    3
    4
    5
    6
    const store = new Vuex.Store({
    state:{
    count: 1
    },
    mutations:{}
    })
  4. 将store实例挂载到Vue实例上,就能全局访问Vuex上的所有数据了:

    1
    2
    3
    4
    5
    6
    7
    new Vue({
    el: '#app',
    router,
    components: { App },
    template: '<App/>',
    store // 挂载store
    })

需求实现

如上,我们new出了一个store实例,在该实例中,定义了一个count的值为1,下面我们试着在组件中是否可以拿到这个count的值。

首先我们要知道怎么在组件中获取store中的数据,我们看到count是在store下的state中定义的,可以把state想像为组件中的data,是专门用来存储数据的。state中的数据在组件中都可以通过this.$store.state.xxx 来获取到,由于之前已经把stores实例挂载到了Vue实例上,所以在任何组件中都可以全局访问,这里 this 可以省略不写,这样count的值我们就可以拿到了。

绑定input

input 是在App.vue定义的,找到input,作数据双向绑定

1
<input type="text" v-model="this.$store.state.count">

这时我们保存代码刷新浏览器可以看到,输入框内的值变为了1 如图
Loding...

在amount组件中获取count

用插值表达式获取count的值

1
2
3
<div class="amount">
<h3>当前的值为{{$store.state.count}}</h3>
</div>

这时我们保存代码刷新浏览器可以看到,当前的值变为了1 如图
Loding...

实现加减功能

先看下效果,如图
Loding...

很简单,只需给两个按钮添加 click 事件,再定义两个方法就可以实现,来看下代码

  1. 绑定事件

    1
    2
    <button @click="cut">-</button>
    <button @click="add">+</button>
  2. 定义方法

    1
    2
    3
    4
    5
    6
    7
    8
    methods:{
    add(){
    this.$store.state.count++;
    },
    cut(){
    this.$store.state.count--;
    }
    }

注意:这样做虽然实现了需求,但是不推荐这么写,因为这样做不符合Vuex的设计理念,而且也不利于数据的维护,为什么这么说呢,来看张图
Loding...

所以,如果要操作 store 中的 state 值,只能通过调用 mutations 提供的方法来操作数据,不推荐直接在组件中操作,因为每一旦导致数据紊乱,不能够快速定位到错误的原因。

mutations属性

经过以上的分析,我们知道操作的方法需要定义在mutations中,来看下代码

1
2
3
4
5
6
7
8
9
10
mutations:{
// 加法
addition(state){
state.count++;
},
// 减法
subtraction(state){
state.count--;
}
}

注意:mutations 中所有方法的第一个参数已经被规定死了,永远是 state

在我们定义了方法后,怎么调用呢?这里,如果组件想要调用mutations中的方法,只能通过 this.$store.commit('xxx') 来调用,其中xxx为方法名。下面我们来调用一下,代码如下

1
2
3
4
5
6
7
8
methods:{
add(){
this.$store.commit('addition');
},
cut(){
this.$store.commit('subtraction');
}
}

看下效果,如图Loding...

mutations中方法如何传参

上面我们已经实现了初始的需求,如果想点击加减按钮,让数值加减的跨度可变该怎么实现呢,这里可以给方法传参,来看代码

1
2
3
4
5
6
7
8
9
10
mutations:{
// 加法
addition(state, num){
state.count += num;
},
// 减法
subtraction(state, num){
state.count -= num;
}
}

接收参数

1
2
3
4
5
6
7
8
methods:{
add(){
this.$store.commit('addition', 5);
},
cut(){
this.$store.commit('subtraction', 3);
}
}

如上我们给 additionsubtraction 除了state外各自传了一个num的参数,并且在组件中调用,下面刷新看是否可以实现’点击加每次加5,点击减每次减3’ 如图Loding...

mutations中方法是否可以传多个参

上面我们传了一个num参数,发现可以,那么是否能够传多个参数呢,来试试吧

1
2
3
addition(state, num1, num2){
state.count += (num1 + num2);
}

调用

1
2
3
add(){
this.$store.commit('addition', 2, 3);
}

运行结果如图Loding...

结果发现,不行,这里我们打印一下第三个参数 console.log(num2) 发现是 undefined 如图
Loding...
Loding...

既然不接受第三个参数,第二个参数变成对象的话会是怎样,来这样试试

1
2
3
4
addition(state, obj){
state.count += (obj.num1 + obj.num2);
// console.log(num2);
}

调用

1
2
3
add(){
this.$store.commit('addition', {num1: 3, num2: 2});
}

运行发现是可以传对象的,结果如图Loding...

结论:所以说 mutations 中方法最多只能有两个参数,其一是state状态对象,第二个是我们通过 commit 提交过来的参数。

getter属性

Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

来看个需求Loding...
我们不想让amount组建中h3中的字符串是手动写死的,怎么实现?

可以用 getter 来实现, 看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const store = new Vuex.Store({
state:{
count: 1
},
mutations:{
// 加法
addition(state, obj){
state.count += (obj.num1 + obj.num2);
// console.log(num2);
},
// 减法
subtraction(state, num){
state.count -= num;
}
},
getters:{
content: (state) => {
return '当前值为:' + state.count;
}
}
})

通过属性($store.getters.xxx)访问,代码

1
2
3
4
<div class="amount">
<!-- <h3>当前的值为{{$store.state.count}}</h3> -->
<h3>{{ $store.getters.content }}</h3>
</div>

看下运行结果,如图Loding...

注意getters 在这里只负责对外提供数据,不负责修改数据。

此外还有Action和Module属性,这里我们的需求没有用到,不多做介绍。

Action属性

Action 类似于 mutation,不同点在于:Action 提交的是 mutation,而不是直接变更状态。Action 可以包含任意异步操作。点我查看Action

Module属性

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

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割。点我查看Module

总结

以上通过一个Demo对Vuex做了简单的了解,在开发中具体场景具体分析是否要用Vuex。官方建议:“如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex”。最后在这里祝大家新年快乐!Happy New Year !

如果觉得文章不错,请我吃根辣条吧~~