前言
学习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的核心概念及基础使用。希望能够帮到读者朋友!如果有什么问题,评论区评论即可。
欢迎关注,后续会带来更多精彩内容!