关于Vuex中相关知识点的总结
Vuex介绍
Vuex是做什么的,有什么作用?应用场景又有哪些,其实在官网已有相应的介绍,这里引用官网的一些观点:
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
作为一个刚毕业的前端萌新,接触到的项目比较少,根据自己实际经历目前我用的的vuex的地方主要存在在管理系统中登录校验通过后,来存储用户的信息,另外由于vuex存储的一个特点在刷新界面时Vuex中的数据会被清空
.那么在用来存储登录过的用户信息是不太适合的或者说Vuex的能力还不足以达到这样一个需求。所以目前我所接触过的管理系统中用来存储用户信息,通常会和js-cookie
这样一个操作cookie的插件来一起使用,通过分发action等一系列操作,最终将数据存储到cookie中去,保证在刷新界面时数据不会丢失。具体怎么实现,继续往后看吧。
Vuex的基础使用
说完了Vuex的基本概念以及自己在实际运用中的应用场景,我们来看下Vuex的基本使用。
可参照博客VueJS中学习使用Vuex详解
Vuex的进阶使用
当一个项目有多个信息需要进行维护,比如用户的个人信息需要进行管理,根据权限不同展示不同的管理系统左侧下拉菜单。这时候如果再使用上面的用法,会造成这个状态管理内容过多,且可读性不强。这时候需要用到另外一种模式。即使用module将store分隔成不同的模块
.使用方法如下。
第一步:在项目中建立一个名为store
的文件夹,该文件夹下下面有一个modules
文件夹和一个index.js
文件,其中modules
文件夹中存在两个文件。分别为collection.js
和footStatus
文件。整个项目结构如图所示。
其中两个js文件的代码如下
//collection.js
const collection = {
//namespace: true 的作用等会来说明
// namespance: true,
state: {//相当于vue实例中的data属性
collects: []
},
getters: { //相当于vue实例中的计算属性
renderCollects(state){ //承载变化的collects
return state.collects;
}
},
mutations:{ //相当于vue实例中methods方法
pushCollects(state,items){ //如何变化collects,插入items
state.collects.push(items)
}
},
actions: { //actions中的方法是用来提交mutations中的方法,因为mutations中的方法要想触发,必须分发actions中的方法才可以。
increment({commit},item){ //触发mutations里面的pushCollects ,传入数据 形参item 对应到items
commit('pushCollects',item);
}
}
//最后将这个js文件给暴露出去
export default collection
// footStatus.js文件
const footStatus = {
// namespace:true, 同上这一行的作用等会来说明
state: {
showFooter: true,
changableNum: 0
},
getters: { //实时监听state值的变化(最新状态)
isShow(state) { //承载变化的showFooter的值
return state.showFooter
},
getChangedNum(state){ //承载变化的changebleNum的值
return state.changableNum
}
},
mutations: {
show(state) { //自定义改变state初始值的方法,这里面的参数除了state之外还可以再传额外的参数(变量或对象);
state.showFooter = true;
},
hide(state) { //同上
state.showFooter = false;
},
newNum(state,sum){ //同上,这里面的参数除了state之外还传了需要增加的值sum
state.changableNum+=sum;
}
},
actions: {
hideFooter(context) { //自定义触发mutations里函数的方法,context与store 实例具有相同方法和属性
context.commit('hide');
},
showFooter(context) { //同上注释
context.commit('show');
},
getNewNum(context,num){ //同上注释,num为要变化的形参
context.commit('newNum',num)
}
}
}
//同前一个js文件一样,将这个js文件给暴露出去
export default footStatus
有了两个js文件之后,需要在store文件夹中的index.js文件中进行引入
//index.js文件
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
import collection from './modules/collection';
import footStatus from './modules/footStatus';
export default new Vuex.Store({
modules:{
collection,
footStatus
}
//有些时候还会有全局的getters、mutations、actions
// getters:{
// ...
// },
// mutations:{
// ...
// },
// actions:{
// ...
// }
})
有了上面几个文件之后,在实际中该如何使用呢,我们不妨来写个例子做个演示使用。如图所示,拟实现的效果,当我们点击button按钮上面的值也跟着变化。如图所示。
当然实际中,这种不可能利用vuex来实现,这里只是借此来演示一下vuex的各种用法。上代码:
<template>
<div class="vuexs-container" style="width: 600px;border:1px solid blue;margin: auto;">
<div style="margin-bottom: 30px;">计算属性:{{collects}}</div>
<button @click="initData">改变store</button>
<p style="margin-top:60px;">{{num}}</p>
</div>
</template>
<script>
export default {
name: 'Vuexs',
data(){
return {
num: 1,
collects: []
}
},
computed: {
},
methods: {
initData(){
//通过this.$store.state后加上指定的模块名称,再引用对应的属性值就可以获取store中存储的值,并且state中的值变化,页面上的值也随着变化。
this.collects = this.$store.state.collection.collects;
//点击一次,向collection.js的collects中加入一个5
this.collects.push(5);
console.log(this.$store.state.collection.collects)
}
}
}
</script>
说到这里可能有人会想,通过这种方式,能够响应的获取到state中的值,为啥还需要用getters
.(之前不是说getters相当于计算属性,就是为了在数据发生变化时实时的更新吗?)
那代码上的这种方式行不行呢?肯定是可以,毕竟已经达到预期的结果了吗,但确不好?原因个人觉得有以下两点:①如果在该界面有多个属性需要获取,我们都用this.$store.state这种方式将它定义在data中,是不是会显得很累赘。②上面的代码,我们可以看到实际上是直接将内存地址赋给了this.collects,作为一个合格的程序员,这种事还是少干的好。你说是吧?
那么问题来了,如果想在组件中使用store中的state属性,具体该怎么做呢,那就不得不提三个流弊的函数 mapSate、mapGetters、mapActions。具体怎么使用,
mapState的使用
我们来看下。还是之前那张图要求实现的效果。看看利用mapState函数的使用方式。
<template>
<div class="vuexs-container" style="width: 600px;border:1px solid blue;margin: auto;">
<div style="margin-bottom: 30px;">计算属性:{{collects}}</div>
<button @click="initData">改变store</button>
<p style="margin-top:60px;">{{num}}</p>
</div>
</template>
<script>
//将需要利用的函数引入
import {mapState,mapMutations,mapActions} from 'vuex';
export default {
name: 'Vuexs',
data(){
return {
num: 1
}
},
//第一种computed写法
computed: {
//mapState返回的是一个对象,利用ES6语法的解构赋值,将其中的属性展开,如果你不想用...mapState这种方式,也没有问题,看第二种computed写法
...mapState({
//collects是自定义计算属性名可以不是collects,后面的collects是collection.js中的collects值
collects: (state) => state.collection.collects
})
},
//第二种computed写法,mapState不是返回一个对象吗,那么我们就将mapState结果赋给computed不就好了。但更推荐用第一种种方式
// computed: mapState({
// collects: (state) => state.collection.collects
// }),
methods: {
initData(){
//点击一次,向collection.js的collects中加入一个5
//下面两种方式任选一个即可
//第一种写法,直接利用this.$store,state来赋值,但当要维护的数据多了这种方式就不太好,太累赘,所以还是建议用第二种方式。
this.$store.state.collection.collects.push(5);
//第二种写法,分发actions中的方法来实现改变state的值
this.$store.dispatch('increament',5) //分发action指定方法然后触发mutations中的指定方法。
}
}
}
</script>
上面就是我对mapState用法的一点简单认识吧。这里补充个之前未解决的问题。在最开始写collection.js
和footStatus.js
时,我们将第一行namespaced:true
给注释掉了,那这一行又是什么作用呢?namespace:true
是用于在全局引用此文里的方法时标识这一个的文件名,还是不明白?那就实际操作一把好了,一切都将明明白白。
将两个js文件的namespace:true
都给加上,然后运行上面这个index.vue
文件对应的文件,发现点击button的时候报错了,提示vuex.esm.js?2f62:419 [vuex] unknown action type: increment
为什么会出现这个情况呢,就是因为namespaced:true
这行导致,当我们加了这行后,它唯一标识了这个文件,无法在全局访问到。那怎么办?最简单的方式就是namespaced:true
这行给去了,那如果我偏偏不去掉这行(就是这么傲娇),怎么办呢?那就改一下分发的方式好了,将最后一行改为
this.$store.dispatch('collection/increment',5)
就是这么简单,也就是你如果加了```namespaced:true``这行,你在分发该模块中的方法时,必须在方法前面加上指定的js文件名称。
mapGetters的使用
说完了mapState的使用,我们来看看mapGetters的用法,同mapSate类似,它对应的是模块中的getters属性,返回的也是一个对象。
拟实现,在向store加入一个数据时,计算出collects第一个值和最后一个值的和。
请看第一种实现方式
<template>
<div class="vuexs-container" style="width: 600px;border:1px solid blue;margin: auto;">
<div style="margin-bottom: 30px;">计算属性:{{collects}}</div>
<button @click="initData">改变store</button>
<p>
<span>{{collects[0]}}</span>+
<span>{{collects[collects.length-1]}}</span>=
<span>{{collectsNum}}</span>
</p>
<p style="margin-top:60px;">{{num}}</p>
</div>
</template>
<script>
import {mapState,mapMutations,mapActions} from 'vuex';
export default {
name: 'Vuexs',
data(){
return {
num: 5
}
},
computed: {
...mapState({
collects: (state)=>state.collection.collects
}),
collectsNum(){ //这个计算属性用来返回相加的结果
var item =this.$store.state.collection.collects;
return (item[0] + item[item.length -1])
}
},
methods: {
initData(){
this.num++;
this.$store.dispatch('collection/increment',this.num);
}
}
}
</script>
通过上面的代码可以实现,但这并不是我们想要的,看上去也不是太好,既然vuex中存在getters,相当于computed,那对vuex中的数据修改我们为何不把他放在vuex模块的getters中去,而放到组件中呢。这也不利于其他组件的复用,如果其他组件还想用这个计算属性,那我们在其他组件中是不是又要重新写一遍,不是增加了重复的代码,看着也很不舒服吗。那怎么办呢?
我们不妨在collections.js的基础上,在getters中添加一个属性,用于返回值相加的结果。
//collection.js
cosnt collection ={
...
getters: {
renderCollects(state){ //承载变化的collects
return state.collects;
},
//计算第一个和最后一个相加结果
collectsSum(state){
return state.collects[0] + state.collects[state.collects.length-1]
}
},
...
}
那这时候在组件中该怎么使用呢,只需要将之前的computed中的collectsNum计算属性换成下面这样就行了
.......
computed: {
...mapState({
collects: (state)=>state.collection.collects
}),
//第一种方式
// collectsNum(){ //这个计算属性用来返回相加的结果
// var item =this.$store.state.collection.collects;
// return (item[0] + item[item.length -1])
// }
//第二种方式
...mapGetters({
//第一个为自定义的计算属性,第二个为collection.js中的getters中存在的属性
collectsNum: 'collectsSum'
})
},
......
是不是看着很舒服,而且可复用性更强。
mapActions的使用
有了前面mapState和mapGetters的举例之后,mapActions是不是也变得很简单了呢,只不过mapActions是放在methods中,除此之外其他用法类似。
<template>
<div class="vuexs-container" style="width: 600px;border:1px solid blue;margin: auto;">
<div style="margin-bottom: 30px;">计算属性:{{collects}}</div>
<!-- <button @click="initData">改变store</button> -->
<button @click="increment(num)">改变store</button>
<p>
<span>{{collects[0]}}</span>+
<span>{{collects[collects.length-1]}}</span>=
<span>{{collectsNum}}</span>
</p>
<p style="margin-top:60px;">{{num}}</p>
</div>
</template>
<script>
import {mapState,mapMutations,mapActions, mapGetters} from 'vuex';
export default {
name: 'Vuexs',
data(){
return {
num: 5
}
},
computed: {
...mapState({
collects: (state)=>state.collection.collects
}),
...mapGetters({
//第一个为自定义的计算属性,第二个为collection.js中的getters中存在的属性
collectsNum: 'collectsSum'
})
},
methods: {
// 之前的方式
// initData(){
// this.num++;
// this.$store.dispatch('increment',this.num);
// }
// 现在的方式,如果没有加namespaced:true这行,就这么使用
...mapActions([
'increment'
])
//如果加了namespaced:true这行,就像下面这样使用,需要指定actions方法所在的模块
// ...mapActions('collection',[
// 'increment'
// ])
}
}
</script>
需要注意一下加了namespaced: true 和没有加namespaced:true时的mapActions
用法上的区别
以上是我对Vuex相关知识点的一个总结和回顾。部分内容参考了博客VueJS中学习使用Vuex详解