Vuex由浅入深详细讲解

前言

学习Vue时,我们经常要涉及到组件之间的通讯。目前已知的组件间通讯方法有很多,如:父子通讯使用props,通过父组件实现多个子组件之间的沟通,子组件之间的通讯使用自定义事件(利用refs也是一种方式),以及事件总线。

目前,事件总线是一种很常用的方式,但是在做项目时如果事件总线太多也会比较麻烦,而且代码容易乱。

本节,将介绍一种更简单的方式来实现各组件之间的通讯——使用vuex,这种方式较上面提到的方式的优点是,使用vuex,能够实现所有组件一起操控同一个数据,这样就避免了通讯,传值的麻烦。

一,理解Vuex

1.1 Vuex是什么

Vuex是专门再Vue中实现集中式状态(数据)管理的一个Vue插件,对Vue应用中多个组件的共享状态进行集中式管理(读、写),也是一种组件间的通信方式,且适用于任意组件间的通讯。

从上述这一段介绍我们可以提取的关键词是:
状态(数据),管理,插件

这三个词较为关键。其中,状态和数据的关系,将在后文解释;而管理和插件,如上概念可知,Vuex其实是一个用于状态管理的插件。

因此,我们对Vuex的初印象可以是:一个可以用于管理Vue中状态(数据)的插件。

1.2 Vuex概述

Vuex不属于任何一个组件,但是其内部的数据可以实现数据的共享。每个组件都可以借助相关的api对内部数据进行操控。

示意图如下:
在这里插入图片描述

1.3 Vuex统一管理状态的好处

能够在Vuex中集中管理共享的数据,易于开发和后期维护;

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

存储在Vuex中的数据都是响应式的,能够事实保持数据与页面的同步。

1.4 什么时候使用Vuex

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

二, Vuex的配置

2.1 安装vuex依赖包及vuex版本问题

在Vue2中,我们用vuex3版本的;在Vue3中,我们用vuex4版本的。否则可能会出现报错。

笔者这里用的是vuex3:

npm i vuex@3

2.2 导入vuex包

在下载完vuex后,上面也介绍了vuex是一个插件,所以我们要在main.js中引入并注册:

import Vuex from 'vuex'
Vue.use(Vuex)

2.3 创建store对象

这里先介绍以下store对象:store译为仓库。store在vuex中用于状态管理。而状态,也可以理解为数据。

store是vuex中一个很重要的部分。

我们需要写一个js文件,内部放置store对象及其相关代码:

在这里插入图片描述

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

export default new Vuex.Store({
    
    
    state: {
    
    },
    mutations: {
    
    },
    actions: {
    
    }
})

store内部的属性,后面都会逐个讲解,但是在开始需要配置上。

2.4 在main.js中挂载store

import Vue from 'vue'
import App from './App.vue'
import store from './store/index'

Vue.config.productionTip = false

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

这里,我要向读者解释,为什么在store中引入vuex,注册vuex,而不是在main.js中:首先我们要知道,一定要在创建一个store实例之前运用Vue.use(Vuex),而在js代码编译的时候,import开头的都会优先编译。也就是说,如果把它们都放在main.js中,store实例一定是先被创建,而后vuex才被注册的。而这样不符合规则,会报错:
Uncaught Error: [vuex] must call Vue.use(Vuex) before creating a store instance

三,Vuex的核心概念

本节内容将围绕一个小的demo来为读者讲述Vuex中的核心概念。

3.1 demo

案例很简单,我设置一个数为count,并且设置两个组件,一个组件用于做count加加,一个组件用于做count减减。两个组件共同操控一个count。

在我的代码中,我设置了两个组件,Jiafa和Jianfa,并且把它们放在了App中,这也是我们的demo最开始的样子。
在这里插入图片描述
在这里插入图片描述
接下来我会围绕上面这个案例,通过一步步把这个案例做完来为读者讲述Vuex的相关知识。

3.2 State

State在Vuex中,提供唯一的公共数据源。所有共享的数据都要同意放到Store的State中进行存储。

在一开始配置中,我们已经在Store对象中配置了State属性。现在,直接把我们需要共享的数据count放到state中即可。
在这里插入图片描述
Vuex有一个特点就是:共享。现在,我们把数据放到了state,就可以实现共享。组件通过访问State,则可以访问到State中内部的数据。

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

在Jiafa组件中访问:
在这里插入图片描述
效果如下:
在这里插入图片描述
以上,便成功实现了组件访问公共数据。在模板语法中,使用$store.state.count即可。但是,如果每个地方都这样写,代码看上去会很复杂,并不符合vue的代码规范。于是,新的方式出现了,那就是mapState。

3.2.1 mapState函数

mapState是vuex中的函数,它可以简化我们从state中获取数据的代码。

在使用前我们需要先引入该函数。因为是组件在使用简化,所以我们要在组件中引入。刚刚我们用第一种方式在加法组件中获取到了count,所以现在我们在减法组件中尝试第二种方法。

第一步,先引入mapState函数:
在这里插入图片描述
第二步,使用mapState函数。
mapState函数可以为我们简化代码量,它的本质实际上是一个计算属性,所以该函数的使用要放在计算属性中,直接把需要简化的数据放入mapState函数中即可。这里要加上三个点,展开运算符,意思是把这里面的数据映射为当前组件的计算属性。
在这里插入图片描述
做完这一切后,直接把count往插值语法中一丢就可以了:
在这里插入图片描述
效果如下:
在这里插入图片描述

3.3 Mutation

3.3.1 引出Mutation之实现加法减法的错误方式

目前,我们已经知道了从state中获取数据的两种方式。既然获取到了数据,我们也具备操作数据的能力。比如我现在想实现对count的自增,直接在获取数据的基础上,设定点击事件即可:
在这里插入图片描述
但是,==在Vuex中,并不允许组件直接修改Store中的数据。==所以这种方式是非常不合法的。那应该如何去对数据进行操作呢?这是我下面需要介绍的内容。

3.3.2 Mutation的使用

Mutation用于变更Store中的数据。

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

且通过这种方式操作会稍微繁琐,但是==可以集中监控数据的变化。如果每一次我们都用之前的错误写法来修改数据,后期不利于数据的维护。

以上就是Mutation的作用及为什么使用Mutation的原因。

以下,我们就来介绍如何用Mutation来对数据作出修改。

在Store的配置文件中,我们首先需要去配置Mutation,在里面配置上与操作数据相关的函数。
在这里插入图片描述
现在,我们需要在组件中触发mutation,触发方式很简单,使用commit函数。使用commit函数可以调用mutation中的方法。

this.$store.commit('add')

在这里插入图片描述
现在我们的组件可以正常使用加法了:
在这里插入图片描述

3.3.3 Mutation传递参数

刚刚我们实现的是,让count自增。但是现在提供新的需求,我不确定要让count自增,我要让count加五。遇到这种情况,需要传参告诉Mutation,到底给count加多少。

所以本节内容,就是告诉读者使用Mutation时如何传递参数。

首先,依旧是对mutation进行配置:

在这里插入图片描述
接着,再去触发mutation,按钮绑定事件:
在这里插入图片描述
然后再用commit去触发mutation:
在这里插入图片描述
在组件中的使用其实与一般函数传参的方式类似。

3.3.4 mapMutations

在学完state后,我们学习了mapState,成功解决了获取数据代码繁琐的问题。现在我们学习mapMutations,目的也一样,为了让代码看上去更简洁,一目了然。

现在,我们来实现减法的功能

首先第一步依旧是配置mutation:
在这里插入图片描述
接着,我们要在组件中使用sub。依旧是先给按钮绑定事件:
在这里插入图片描述
然后,我们需要引入mapMutations:
在这里插入图片描述
接着,调用mapMutations,和之前原理类似,依旧要写展开符,并调用其内部的sub函数。
在这里插入图片描述
这里要注意,…mapMutations必须要放在methods,而不是计算属性中。因为我们的subhandle需要用到它。

接着,我们再用类似方法实现传参:

依旧是先在mutations中配置:
在这里插入图片描述
接着给按钮绑定事件:
在这里插入图片描述
最后再调用:
在这里插入图片描述

3.4 进阶版demo

以上,我们已经实现了对count的基本操作:读取count,对count进行加法减法操作;

现在,提出一个进阶版的要求:想要实现点击按钮,等待一秒的时间后对count‘值进行加一。

这里很显然,我们需要一个定时器,而定时器算的上是一个异步任务。

读者朋友可以试着在mutations的函数中,加定时器。但是最后的结果是,页面发生变化,但是Vue的浏览器调试器中没办法观察到count的变化。

在mutations中不能执行异步操作,vuex无法正常识别。

如果我们要进行异步操作,该怎么做?后续的内容将围绕这个问题进行讲解。

3.5 Action

在Vuex中,Action用于处理异步任务。

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

3.5.1 Action的使用

首先,依旧是在store文件中配置actions,内部有一个函数,函数内部写一个定时器,在actions中依旧需要通过commit来调用mutations中的函数,所以我们给addAsync一个参数,让这个参数去调用commit。这里注意,在actions中也无法修改数据,只有mutations中定义的函数有权利去修改数据。

在这里插入图片描述

这里我们定义好了actions,现在我们需要在组件中使用它。

首先,先给按钮绑定一个事件:
在这里插入图片描述
接着,在addasync中触发定时器,我们需要触发Actioin。前面我们学习,触发mutations中的函数需要用到commit,而触发actions,我们需要用到dispatch。
在这里插入图片描述

3.5.2 如何在使用actions的时候携带参数

刚刚我们在写addAsync函数的时候,在内部通过调用commit来操作add。现在我们想实现的是,让addasync加五。联系到之前的讲解,可以敏锐的察觉到这边使用actions的时候需要携带参数,并且依旧是延迟一秒钟后再做加法运算。

首先,依旧是编写actions,这里调用我们刚刚写好的addfive函数:
在这里插入图片描述

接着就是在组件中使用了,依旧是设置一个按钮并给按钮绑定事件:

在这里插入图片描述

接着再编写事件addasyncfive:
在这里插入图片描述
最后即可正常使用。

3.5.3 mapActions

与之前的类似,mapActions是触发actions的第二种方式。

这次我们来实现减法中延迟一秒对count减5的操作。

第一步依旧是先配置actions

在这里插入图片描述

接着在组件中使用,依旧是先引入mapActions函数,并在methods中使用它进行声明,别忘了展开符:
在这里插入图片描述
然后设置按钮,编写按钮的绑定事件:
在这里插入图片描述

在这里插入图片描述

3.6 Getter

Getter用于对Store中的数据进行加工处理形成新的数据,类似于Vue的计算属性。

Store数据发生变化,Getter数据也会发生变化。

但是Getter不会修改State的数据,它只起到一个包装的作用。

所以本节讲述vuex最后一个核心概念:Getter。

3.6.1 Getter的使用

首先是对Getter的配置。

这里定义了一个getter:
在这里插入图片描述
接着使用getters有两种方式,第一种:

this.$store.getters.名称

在App中使用:
在这里插入图片描述
可以正常显示:
在这里插入图片描述

3.6.2 mapGetters

依旧先引入
在这里插入图片描述
在计算属性中放置:
在这里插入图片描述
最后使用即可:
在这里插入图片描述
效果依旧:
在这里插入图片描述

四,代码部分

main.js

import Vue from 'vue'
import App from './App.vue'
import store from './store/index'

Vue.config.productionTip = false

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

App.vue

<template>
  <div>
    <Jiafa></Jiafa>
    <Jianfa></Jianfa>
    <!-- {
    
    {
    
     $store.getters.showNum }} -->
    {
    
    {
    
     showNum }}
  </div>
</template>

<script>
import Jiafa from '@/components/Jiafa.vue'
import Jianfa from '@/components/Jianfa.vue'
import {
    
     mapGetters } from 'vuex';
export default {
    
    
  name: 'App',
  components: {
    
    Jiafa, Jianfa},
  computed: {
    
    
    ...mapGetters(['showNum'])
  },
}
</script>

<style>

</style>

store-index.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

export default new Vuex.Store({
    
    
    state: {
    
    
        count: 0
    },
    mutations: {
    
    
        add(state) {
    
    
            //加加
            state.count++
        },
        addfive(state, i) {
    
    
            // 加五
            state.count += i;
        },
        sub(state) {
    
    
            //减减
            state.count--
        },
        subfive(state, i) {
    
    
            // 减五
            state.count -= i
        }
    },
    actions: {
    
    
        addAsync(context) {
    
    
            setTimeout(() => {
    
    
                context.commit('add')
            }, 1000)
        },
        addAsyncfive(context, i) {
    
    
            setTimeout(() => {
    
    
                context.commit('addfive', i)
            }, 1000)
        },
        // 减减延迟1s
        subAsync(context) {
    
    
            setTimeout(() => {
    
    
                context.commit('sub')
            }, 1000)
        },
        // 减5延迟1秒
        subAsyncfive(context, i) {
    
    
            setTimeout(() => {
    
    
                context.commit('subfive', i)
            }, 1000)
        },
    },
    getters: {
    
    
        showNum(state) {
    
    
            return '当前最新的数量是['+ state.count + ']';
        }
    }
})

Jiafa

<template>
    <div>
        <div>当前最新的count的值为:{
    
    {
    
     $store.state.count }}</div>
        <button @click="add">+1</button>
        <button @click="addfive()">+5</button>
        <button @click="addasync">延迟一秒后+1</button>
        <button @click="addasyncfive(5)">延迟一秒后加五</button>
    </div>
</template>

<script>
export default {
    
    
    name: 'Jiafa',
    methods: {
    
    
        add() {
    
    
            this.$store.dispatch('addAsync')
            this.$store.commit('add')
        },
        addfive() {
    
    
            this.$store.commit('addfive', 5)
        },
        // 延迟一秒后再加
        addasync() {
    
    
            this.$store.dispatch('addAsync')
        },
        addasyncfive(i) {
    
    
            this.$store.dispatch('addAsyncfive', i)
        }
    }
}
</script>

<style></style>

Jianfa

<template>
    <div>
        <div>当前最新的count的值为:{
    
    {
    
     count }}</div>
        <button @click="subhandle">-1</button>
        <button @click="subi(5)">-5</button>
        <button @click="subasync">延迟一秒减一</button>
        <button @click="subasyncfive(5)">延迟一秒减5</button>
    </div>
</template>

<script>
// 引入mapState
import {
    
    mapMutations, mapState, mapActions} from 'vuex'
export default {
    
    
    name: 'Jianfa',
    computed: {
    
    
        ...mapState(['count']),
    },
    methods: {
    
    
        ...mapMutations(['sub', 'subfive']),
        ...mapActions(['subAsyncfive', 'subAsync']),
        subhandle() {
    
    
            this.sub()
        },
        subi(i) {
    
    
            this.subfive(i)
        },
        //延迟1s减一
        subasync() {
    
    
            this.subAsync()
        },
        subasyncfive(i) {
    
    
            // 延迟1s减5
            this.subAsyncfive(i)
        }
    }                                                                     
}
</script>

<style></style>

后记

以上就是Vuex的核心概念及基础使用。希望能够帮到读者朋友!如果有什么问题,评论区评论即可。

欢迎关注,后续会带来更多精彩内容!

猜你喜欢

转载自blog.csdn.net/zxdznyy/article/details/128899867