Vuex技术

1、vuex

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

优点分析:全局事件总线是在面对较多的组件之间都需要共享数据,即一起读、写数据时会写很多的$bus.$on和$bus.$emit,优势不如vuex简单

 

 

 1、案例

vuex求和案例

实现我们使用之前学习的vue简单实现需求,代码如下

//App组件代码
<template>
	<div>
		<Count/>
	</div>
</template>

<script>
	import Count from './components/Count'
	export default {
		name:'App',
		components:{Count},
	}
</script>
//count组件代码
<template>
	<div>
		<h1>当前求和为:{{sum}}</h1>
		<!-- 注意n一开始是数字1,但是选择时的值下面都是“2”这种字符串,会导致变成字符串运算 -->
		<!-- 要么变成:value="",使得其值未js对象当中数字1;要么v-model.number强制类型转化 -->
		<select v-model.number="n">
			<option value="1">1</option>
			<option value="2">2</option>
			<option value="3">3</option>
		</select>
		<button @click="increment">+</button>
		<button @click="decrement">-</button>
		<button @click="incrementOdd">当前求和为奇数再加</button>
		<button @click="incrementWait">等一等再加</button>
	</div>
</template>

<script>
	export default {
		name:'Count',
		data() {
			return {
				n:1, //用户选择的数字
				sum:0 //当前的和
			}
		},
		methods: {
			increment(){
				this.sum += this.n
			},
			decrement(){
				this.sum -= this.n
			},
			incrementOdd(){
				if(this.sum % 2){
					this.sum += this.n
				}
			},
			incrementWait(){
				setTimeout(()=>{
					this.sum += this.n
				},500)
			},
		},
	}
</script>

<style lang="css">
	button{
		margin-left: 5px;
	}
</style>
2、Vuex工作原理

不难发现,上面的图中,(‘jia’,2)感觉没有经过Actions的必要,完全可以直接传到Mutations进行计算。

1、Actions作用:当1只有动作类型'jia'时,具体数据需要向服务器获取,此时Actions复制向服务器发送ajax请求获取数据

2、不需要向服务器获取获取数据时Vue Components可以直接commit('jia',2)到Mutations,

只是图中没有体现,是允许直接commit,跳过Actions

可以理解为客人=组件、服务员=Actions、厨师=Motations、菜=States

客人向服务员点菜,服务员转信息给厨师,厨师做菜,再给客人

客人可以直接取后厨找厨师点菜,跳过服务员

换菜单了,客人没法点菜,或者菜单中一些菜不够,或者矛盾(即数据需要处理)需要服务员处理才行

 

 3、搭配vuex环境

1、安装vuex:npm i vuex(默认是安装的vuex4,需要vue3的版本)

      显示所有版本号:npm view vuex versions

      指定安装版本号:npm install vuex@3.6.2 --save

      vue2使用    vuex3

      vue3使用    vuex4

2、main.js当中使用插件:vue.use(Vuex)

3、完成store来管理其中的三个对象

4、让所有的vc都可以看见store

 vue官方推荐新建src/store/index.js来创建store

//src/store/index.js
//该文件用于创建Vuex中最为核心的store
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)

//准备actions——用于响应组件中的动作
const actions = {}
//准备mutations——用于操作数据(state)
const mutations = {}
//准备state——用于存储数据
const state = {}

//创建并暴露store。暴露即导出,即其他文件可以导入该js文件
export default new Vuex.Store({
	//actions:actions,的简写,第一个actions是store固定内容,第二个是上面定义的数据,
	//es6中,对象的key和value相同就可以简写
	actions,
	mutations,
	state,
})

然后在main.js内引入store,下面是部分代码

//引入store,没说引入store文件夹的哪一个文件默认导入index.js
import store from './store'
//创建vm,引用store,这样在App和Count组件都可以访问store
new Vue({
	...
	store
})

 Tips:此次我们引入vue、vuex都是在创建stores的index.js里面。

1、vue中引入插件时会先执行完插件

2、代码中存在引入的import代码,无论其在代码何处,其执行优先级最高,优先执行

此处创建store实例前必须先引入vue和vuex,并且use使用

但是如果和之前一样在main.js内引入的话:

import Vue from 'vue'

import Vuex from 'vuex'

import store from './store'

Vue.use(Vuex)

无论import在哪里,引入store.js都会在Vue.use(Vuex)之前执行,但是store.js当中创建

Vuex实例new Vuex.Store({})都需要先use(Vuex)才能行。所以说上面都是把引入和使用的代码放在index.js内部。

上面提到了App和Count组件都可以访问到store,即可以正常访问上面原理图中store内的Api

尝试输出上面的结果发现:现在就ok了

4、案例vuex版本

现在我们数据还在Count组件内,使用vuex我们必须把数据放到store下的state当中

vuex共享数据,也就是说至少有两个组件都向vuex读写数据才交共享,上面App内只有一个组件,没法完美显示vuex优点(因为数据sum放在Count和vuex没区别,必须要多个共享sum),不过此时我们先就让Count一个组件可以正常读写vuex内的data即可,第二个组件后面加

1、把数据sum放入state当中,之前在Count中

所以说Count当中html代码要改,并且methods内运算函数也要改,都是因为无法向之前一样访问sum数据了

2、然后就是点击事件和数据渲染都是在Count组件内实现,跳过点击事件和$store.

 首先是Count组件代码,和vue版本相比只修改了js代码,其中的JIA和JIAN这些来自下面的store

<script>
	export default {
		name:'myCount',
		data() {
			return {
				n:1, //用户选择的数字
			}
		},
		methods: {
     //注意前两个加法和减法,因为没有任何的逻辑处理之类的
     //所以说可以跳过action,直接commit到Mutation,但是注意Mutation当中是大写的JIA和JIAN
			increment(){
				this.$store.commit('JIA',this.n)
			},
			decrement(){
				this.$store.commit('JIAN',this.n)
			},
			incrementOdd(){
				this.$store.dispatch('jiaOdd',this.n)
			},
			incrementWait(){
				this.$store.dispatch('jiaWait',this.n)
			}
		},
		mounted() {
			console.log('myCount',this)
		},
	}
</script>

 store注意事项:

1、actions即服务员,可以处理业务逻辑,如下面的定时器和判断奇偶

      mutations即厨师,里面只能直接加工数据,即直接加减乘除

2、actions内补虚药处理业务逻辑的可以直接跳过到mutations内,但是mutations内也要写上相应的加工方法。注意和上面的Count组件联系起来,加减法是直接通过commit传到Mutations内,并且加减的函数是按照mutations内的JIA和JIAN为准

2、注意sctions和mutations的第一个参数context和state

//该文件用于创建Vuex中最为核心的store
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)

//准备actions——用于响应组件中的动作
const actions = {
	//注意:不难发现,jia和jian没有意义,属于上面原理图中可以跳过的部分,所以说可以注释掉
	// jia:function(context,value){   下面是简写,参数context当中有commit、dispatch、state(数据)
	// jia(context,value){	
	// 	context.commit('JIA',value)
	// },
	// jian(context,value){
	// 	context.commit('JIAN',value)
	// },
	jiaOdd(context,value){
		if(context.state.sum % 2){
			context.commit('JIA',value)
		}	
	},
	jiaWait(context,value){
		setTimeout(() => {
			context.commit('JIA',value)
		}, 500)
	},
}
//准备mutations——用于操作数据(state),此处的是习惯大写的JIA作为区分,所以说上面commit内传的时候也是JIA
//JIA()中的两个参数state和value分别是sum和传入的参数value的值,Count组件渲染的sum就是来自于这里
const mutations = {
	JIA(state,value){
		state.sum += value
	},
	JIAN(state,value){
		state.sum -= value
	}
}
//准备state——用于存储数据
const state = {
	sum:0  //当前的和
}

//创建并暴露store。暴露即导出,即其他文件可以导入该js文件
export default new Vuex.Store({
	//actions:actions,的简写,第一个actions是store固定内容,第二个是上面定义的数据,
	//es6中,对象的key和value相同就可以简写
	actions,
	mutations,
	state,
})
5、vuex开发工具和部分细节

1、开发者工具,以Edge浏览器为例:

2、context含义

 我们尝试在、‘

、index.js的Action部分输出context,观察其作用

jiaOdd(context,value){
	console.log('actions中的jiaOdd被调用了',context)
    ...
}

dispatch函数是Vuex中一个被广泛使用的函数,它用于触发actions中的函数。

diapatch派遣;派出;发出

 那么为什么Action当中的dispatch是来触发它自身的函数的呢?

当需要进行的处理逻辑复杂,或者需要多个函数处理时,就可以在Action内的函数间使用

 3、既然action的context有sum,为什么不直接在action里面加减

4、为什么业务逻辑代码都是在action当中,明明Count组件内也可以

也就是直接写在Count组件的methods内,但是要考虑到该案例中逻辑处理简单还好说,复杂了的话就代码冗余。外加如果逻辑处理代码需要被多次使用,这样代码复用率低

6、store之getters配置项 

以上面例子为例,我们需要把最后的sum再进行运算然后显示在页面当中

(1)、直接写运算代码

<h1>当前求和为:{{$store.state.sum}}</h1>
<h1>当前求和为:{{$store.state.sum * 10}}</h1>

(2)、计算属性

<h1>当前求和的十倍为:{{ bigSum }}</h1>
<script>
    ...
    computed:{
        bigSum(){
            return $store.state.sum * 10
        }
    }
</script>

(3)、getters配置项

运算简单的时候使用上面两个都无所谓,但是当运算复杂,且需要复用的时候便不推荐,getters是最合适的。

//Count组件内
<h1>当前求和放大十倍为:{{$store.getters.bigSum}}</h1>

//index.js代码内。getters是和action、motation同一级的
//准备getters——用于将state中的数据进行加工
const getters = {
	bigSum(state){
		return state.sum*10
	}
}

不难发现:虽然都是对数据进行一定的处理

计算属性只对其所属部分的组件代码有效

getters配置项是全局有效

7、四个map方法的使用

不难发现,上面的Count组件代码可以优化:

 我们可以考虑在html代码中只写sum和subject,school,然后在计算属性中返回完整值

computed:{
	//靠程序员自己亲自去写计算属性
	sum(){
		return this.$store.state.sum
	},
	school(){
		return this.$store.state.school
	},
	subject(){
		return this.$store.state.subject
	},
	bigSum(){
		return this.$store.getters.bigSum
	},
}

 但是我们又发现计算属性中sum、school、subject又是高度相似都来自于state,bigSum来自getters。还可以优化

1、mapState方法:用于帮助我们映射state中的数据为计算属性

computed: {
    //借助mapState生成计算属性:sum、school、subject(对象写法)
     ...mapState({sum:'sum',school:'school',subject:'subject'}),         
    //借助mapState生成计算属性:sum、school、subject(数组写法)
    ...mapState(['sum','school','subject']),
},

2、mapGetters方法:用于帮助我们映射getters中的数据为计算属性

computed: {
    //借助mapGetters生成计算属性:bigSum(对象写法)
    ...mapGetters({bigSum:'bigSum'}),

    //借助mapGetters生成计算属性:bigSum(数组写法)
    ...mapGetters(['bigSum'])
},

 所以说最后的代码如下:mapstate因为是对象,computed也是对象,所以说前面要加上...表示把sum、school、subject依次取出放入mapState

 

<h1>当前求和为:{{sum}}</h1>
<h3>当前求和放大10倍为:{{bigSum}}</h3>
<h3>我在{{school}},学习{{subject}}</h3>

computed:{
	//借助mapState生成计算属性,从state中读取数据。(对象写法)
    //下面第一个sum对应上面html中的sum,第二个sum是要去找state里面的sum。不能简写,两个虽然相同,但是都是字符不是变量(两个可以不同,即上面html当中和state当中不一样)
	...mapState({sum:'sum',school:'school',subject:'subject'}),

	//借助mapState生成计算属性,从state中读取数据。(数组写法)
    //这才是上面对象的真正的简写方式(只有相同才能简写),记住即可
	...mapState(['sum','school','subject']),

	//借助mapGetters生成计算属性,从getters中读取数据。(对象写法)
	...mapGetters({bigSum:'bigSum'})
	
	//借助mapGetters生成计算属性,从getters中读取数据。(数组写法)
	...mapGetters(['bigSum'])
}

然后是Count组件内的methods内的函数写法可以优化,和上面基本相似

对象写法:

注意此时因为运算是由参数n传递到mutation或者action当中的,所以说html当中点击事件不需要加上参数,mapMutations和mapActions会自动传过去

<template>
	<div>
		<button @click="increment(n)">+</button>
		<button @click="decrement(n)">-</button>
		<button @click="incrementOdd(n)">当前求和为奇数再加</button>
		<button @click="incrementWait(n)">等一等再加</button>
	</div>
</template>

<script>
	import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
    export default {    
        ...
		methods: {
			//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
			...mapMutations({increment:'JIA',decrement:'JIAN'}),

			//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)
			...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
		}
	}
</script>

 数组写法:

在mapState和mapGetters当中可以知道,数组写法是两个相同才能写。但是上面却是:

increment:'JIA',decrement:'JIAN'

所以说要使用数组写法,只有先变成一样,所以说要修改html当中点击事件的名字

<template>
	<div>
		<button @click="JIA(n)">+</button>
		<button @click="JIAN(n)">-</button>
		<button @click="jiaOdd(n)">当前求和为奇数再加</button>
		<button @click="jiaWait(n)">等一等再加</button>
	</div>
</template>

<script>
	import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
    export default {    
        ...
		methods: {
			//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法)
			...mapMutations(['JIA','JIAN']),

            //借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)
			...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
		}
	}
</script>
  1. mapActions方法:用于帮助我们生成与actions对话的方法,即:包含$store.dispatch(xxx)的函数

    methods:{
        //靠mapActions生成:incrementOdd、incrementWait(对象形式)
        ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
    ​
        //靠mapActions生成:incrementOdd、incrementWait(数组形式)
        ...mapActions(['jiaOdd','jiaWait'])
    }
  2. mapMutations方法:用于帮助我们生成与mutations对话的方法,即:包含$store.commit(xxx)的函数

    methods:{
        //靠mapActions生成:increment、decrement(对象形式)
        ...mapMutations({increment:'JIA',decrement:'JIAN'}),
        
        //靠mapMutations生成:JIA、JIAN(对象形式)
        ...mapMutations(['JIA','JIAN']),
    }

备注:mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象

8、多组件共享 

目标

之前就说到,共享讲的是多个组件读写同一个数据,所以说上面只有一个组件的案例还不够全面

再写一个Person组件,展示sum和persons(人员信息)。同理在Count当中也展示sum和persons

重点就是在Person和Count两个组件都可以访问store内的数据:人员信息personList和求和sum两个值。

为此我们在两个组件内都this.$store.state(计算属性内)或者$store.state(div中)即可正常访问数据

unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。

首先需要在index.js当中配置:

 myPerson组件代码如下所示:

<template>
	<div>
        //为了简洁,下面的sum和person都是写在计算属性内的
        //注意这里就可以直接访问sum和personList
		<h1>人员列表</h1>
		<h3 style="color: red;">Count组件求和为{{ sum }}</h3>
		<input type="text" placeholder="请输入名字" v-model="name">
		<button @click="add">添加</button>
		<ul>
			<li v-for="p in personList" :key="p.id">{{p.name}}</li>
		</ul>
	</div>
</template>

<script>
    //nanoid生成唯一的一串字符编码,用于下面用户信息的id
	import {nanoid} from 'nanoid'
	export default {
		name:'myPerson',
		data() {
			return {
				name:''
			}
		},
        //计算属性内没什么重复的,所以说没有像之前和下面Count一样简写
		computed:{
			personList(){
				return this.$store.state.personList
			},
			sum(){
				return this.$store.state.sum
			}
		},
		methods: {
			add(){
				const personObj = {id:nanoid(),name:this.name}
				//console.log(personObj);   输出personObj对象,包括随机的id和输入框的名字
				//因为没有任何的逻辑处理,所以说数据直接commit到mutation当中
                this.$store.commit('ADD_PERSON',personObj)
                //完事后清空
				this.name = ''
			}
		}
	}
</script>

Count组件就简单修改即可:

<h3 style="color:red">Person组件的总人数是:{{personList.length}}</h3>

<script>
	import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
	export default {
		...
		computed:{
			//借助mapState生成计算属性,从state中读取数据。(数组写法)
			...mapState(['sum','school','subject','personList']),
			//借助mapGetters生成计算属性,从getters中读取数据。(数组写法)
			...mapGetters(['bigSum'])
		},
		methods: {
			...
		}
</script>
9、vuex模块化+namespace
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值