第5章:vuex

第5章:vuex

  • [1 求和案例 纯vue版](#1 求和案例 纯vue版)
  • [2 vuex工作原理图](#2 vuex工作原理图)
  • [3 vuex案例](#3 vuex案例)
  • [4 getters配置项](#4 getters配置项)
    • [4.1 细节](#4.1 细节)
    • [4.2 源代码](#4.2 源代码)
  • [5 mapState与mapGetters](#5 mapState与mapGetters)
    • [5.1 总结](#5.1 总结)
    • [5.2 细节分析](#5.2 细节分析)
    • [5.3 源代码](#5.3 源代码)
  • [6 mapActions与mapMutations](#6 mapActions与mapMutations)
    • [6.1 总结](#6.1 总结)
    • [6.2 细节](#6.2 细节)
    • [6.3 源代码](#6.3 源代码)
  • [7 多组件共享数据](#7 多组件共享数据)
    • [7.1 细节](#7.1 细节)
    • [7.2 源代码](#7.2 源代码)
  • [8 vuex模块化 + namespace](#8 vuex模块化 + namespace)
    • [8.1 总结](#8.1 总结)
    • [8.2 第一部分](#8.2 第一部分)
    • [8.3 第二部分](#8.3 第二部分)
    • [8.4 源代码](#8.4 源代码)

1 求和案例 纯vue版

这里只需要住一个问题,就是Count组件的v-model.number="n",如果没有number,这个n就是个字符串。
src/components/Count.vue

javascript 复制代码
<template>
	<div>
		<h1>当前求和为:{{sum}}</h1>
		<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>

src/App.vue

javascript 复制代码
<template>
	<div>
		<Count/>
	</div>
</template>

<script>
	import Count from './components/Count'
	export default {
		name:'App',
		components:{Count},
	}
</script>

src/main.js

javascript 复制代码
//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//引入插件
import vueResource from 'vue-resource'
//关闭Vue的生产提示
Vue.config.productionTip = false
//使用插件
Vue.use(vueResource)

//创建vm
new Vue({
	el:'#app',
	render: h => h(App),
	beforeCreate() {
		Vue.prototype.$bus = this
	}
})

2 vuex工作原理图

以求和案例为例,用sum来表示当前的和,vuex会交时state保管,此时sum为0。

  • State是一个Object类型对象,可以存储很多数据,例如todos,sum等。

接下来过程如下:

  • Count组件调用dispatch,它是一个函数,要传两个参数,一个是进行的动作类型,一个是数据,即dispatch('jia',2)。
  • Actions也是一个Object类型对象,此时Actions对象里面必然有一组key value为jia:function,此时函数被调用,就收到了2,function函数里面就会调用commit('jia',2)。
  • Mutations也是一个Object类型对象,此时Mutations对象里面必然有一组key value为jia:function,function会拿到两个东西,一个是state,一个是2,随后function里面就写state.sum += 2即可,然后底层就自动走了Mutate。
  • 最终state里面保存的sum的值就变为了2。
  • vuex会重新解析组件,再重新渲染页面,于是页面的sum也变成了2

Actions设计的目的是为了和后端交互的,例如dispatch('chu'),有动作类型,但没有所对应的值,此时就要问后端了,后端服务器返回9,如下:

如果你并不需要和后端交互,就可以直接和Mutations交互,如下:

注意到Devtools即开发者工具是和Mutations进行交互的。

Actions、Mutations和State统一经过一个东西的管理,如下:

也就是说调用dispatch等方法时,是由store提供的,如下:

3 vuex案例

3.1 搭建vuex环境

2022年2月7日,Vue3已经成为了默认版本,vuex也同时更新到4版本,即执行命令:npm i vuex,安装的是vuex的4版本,而vuex的4版本只能在vue3使用。

  • Vue2中,要用vuex的3版本,执行命令:npm i vuex@3
  • Vue3中,要用vuex的4版本

错误写法

首先在src下面创建一个store文件夹,如下:

index里面代码如下:

紧接着在main.js引入store,由于文件名称是index.js,所以可以直接省略名字,脚手架认识,如下:

此时好像环境搭建完毕,但这样会出错,如下:创建store实例之前就要使用Vue.use(Vuex)

回到main.js代码分析,首先得把绿色框文件里得代码执行完了,我才能收到store,随后才走粉色框代码。

  • 这样就导致了先创建store实例,因为绿色框文件即index.js创建了一个store实例。

哪怕换顺序也没用,import语句有优先级,会优先执行,不管顺序。

  • 即首先扫描所有import语句,按照import语句代码顺序,全部先执行import语句。

正确写法

首先在src下面创建一个store文件夹,如下:

index里面代码如下:

紧接着在main.js引入store,由于文件名称是index.js,所以可以直接省略名字,脚手架认识,如下:

3.2 求和案例vuex版

细节分析

一般来说共享是由两个及以上的组件才叫共享,如下:

但在这个案例中仅仅使用了Count组件,主要是为了学习vuex的开发流程。

细节一:actions

actions中函数的第一个参数是context,称之为miniStore,其内容如下:

actions中函数的第二个参数就是所传递的值。

一般commit的时候会大写JIA,目的是做个区分,一看到大写JIA,就知道是mutations里的。

细节一:mutations

mutations中函数的第一个参数是state,并且进行了加工,加上了get和set,如下:

mutations中函数的第二个参数就是所传递的值。

细节三

如下所示,绿色框函数要作一些判断,它是有存在意义的,而红色框函数没有任何存在意义,因此删掉

删掉之后,直接调用commit即可,如下:

源代码

src/components/Count.vue

javascript 复制代码
<template>
	<div>
		<h1>当前求和为:{{$store.state.sum}}</h1>
		<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, //用户选择的数字
			}
		},
		methods: {
			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('Count',this)
		},
	}
</script>

<style lang="css">
	button{
		margin-left: 5px;
	}
</style>

src/store/index.js

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

//准备actions------用于响应组件中的动作
const actions = {
	/* jia(context,value){
		console.log('actions中的jia被调用了')
		context.commit('JIA',value)
	},
	jian(context,value){
		console.log('actions中的jian被调用了')
		context.commit('JIAN',value)
	}, */
	jiaOdd(context,value){
		console.log('actions中的jiaOdd被调用了')
		if(context.state.sum % 2){
			context.commit('JIA',value)
		}
	},
	jiaWait(context,value){
		console.log('actions中的jiaWait被调用了')
		setTimeout(()=>{
			context.commit('JIA',value)
		},500)
	}
}
//准备mutations------用于操作数据(state)
const mutations = {
	JIA(state,value){
		console.log('mutations中的JIA被调用了')
		state.sum += value
	},
	JIAN(state,value){
		console.log('mutations中的JIAN被调用了')
		state.sum -= value
	}
}
//准备state------用于存储数据
const state = {
	sum:0 //当前的和
}

//创建并暴露store
export default new Vuex.Store({
	actions,
	mutations,
	state,
})

src/App.vue

javascript 复制代码
<template>
	<div>
		<Count/>
	</div>
</template>

<script>
	import Count from './components/Count'
	export default {
		name:'App',
		components:{Count},
		mounted() {
			// console.log('App',this)
		},
	}
</script>

src/main.js

javascript 复制代码
//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//引入插件
import vueResource from 'vue-resource'
//引入store
import store from './store'

//关闭Vue的生产提示
Vue.config.productionTip = false
//使用插件
Vue.use(vueResource)

//创建vm
new Vue({
	el:'#app',
	render: h => h(App),
	store,
	beforeCreate() {
		Vue.prototype.$bus = this
	}
})

4 getters配置项

4.1 细节

当前有一个新需求:还要显示当前求和放大十倍后的结果。

最好不要像如下这样写,考虑一下,假设程序员要对state的sum进行一些加工,而且是一些很复杂的数学运算,而且很多程序员要使用这样的功能,这样就不适合像如下这样写。

  • 使用计算属性也不行,它不能跨组件

直接在store的index文件夹进行配置,如下:

  • state就如同data,而getters就如同computed一样

组件中读取数据:$store.getters.bigSum。直接看代码即可

4.2 源代码

在求和案例vuex版基础上,要修改的代码有:
src/components/Count.vue

javascript 复制代码
<template>
	<div>
		<h1>当前求和为:{{$store.state.sum}}</h1>
		<h3>当前求和放大10倍为:{{$store.getters.bigSum}}</h3>
		<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, //用户选择的数字
			}
		},
		methods: {
			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('Count',this.$store)
		},
	}
</script>

<style lang="css">
	button{
		margin-left: 5px;
	}
</style>

src/store/index.js

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

//准备actions------用于响应组件中的动作
const actions = {
	/* jia(context,value){
		console.log('actions中的jia被调用了')
		context.commit('JIA',value)
	},
	jian(context,value){
		console.log('actions中的jian被调用了')
		context.commit('JIAN',value)
	}, */
	jiaOdd(context,value){
		console.log('actions中的jiaOdd被调用了')
		if(context.state.sum % 2){
			context.commit('JIA',value)
		}
	},
	jiaWait(context,value){
		console.log('actions中的jiaWait被调用了')
		setTimeout(()=>{
			context.commit('JIA',value)
		},500)
	}
}
//准备mutations------用于操作数据(state)
const mutations = {
	JIA(state,value){
		console.log('mutations中的JIA被调用了')
		state.sum += value
	},
	JIAN(state,value){
		console.log('mutations中的JIAN被调用了')
		state.sum -= value
	}
}
//准备state------用于存储数据
const state = {
	sum:0 //当前的和
}
//准备getters------用于将state中的数据进行加工
const getters = {
	bigSum(state){
		return state.sum*10
	}
}

//创建并暴露store
export default new Vuex.Store({
	actions,
	mutations,
	state,
	getters
})

5 mapState与mapGetters

5.1 总结

5.2 细节分析

我们多配置两个数据,分别为school和subject,如下:当使用state的很多数据时候,会多次调用store.state.xxx,这样很麻烦。

最简单的方式是用计算属性解决,但是要程序员一次一次的配置,也会很麻烦。

vuex的设计者提供了mapState方法,它可以帮我们批量生成粉色框的代码,因为它们共同点都是从state读取数据。

首先要引入mapState,如下:
看看mapState输出是什么

比如要生成state的sum相对应函数,如下:

我们可以写简写形式,即前面的'he'去掉'',但sum不行,它是一个表达式,其余的也同理,如下:

输出x,可以看到,它为我们生成了函数,如下:

在computed里面配置mapstate

我们会发现,像上述这样配置,会发生报错,这是因为mapState本身是一个对象,computed又是对象,不可能直接在对象里面写对象。
在对象里面写对象的方法

先看一个例子

直接在对象前面加三个点,意思是把obj2里面的每一组key和value展开放入到obj里面

最终输出如下:

因此我们要在mapState加三个点,如下:

还有一种数组写法,并且要求同名,如下:

由于同名可以简写,简写的时候必须要'',以sum:'sum'为例,如果简写成sum,最终含义就是sum:sum,进而去读取sum变量,这就报错了。

举个例子,只有是变量的时候,简写才能直接不要'',如下:

由于a是变量,所以简写不需要''

所以最终简写形式如下:

对于getters同理也有mapGetters,一样的用法,直接看代码即可。

5.3 源代码

在求和案例vuex版基础上,要修改的代码有:
src/components/Count.vue

javascript 复制代码
<template>
	<div>
		<h1>当前求和为:{{sum}}</h1>
		<h3>当前求和放大10倍为:{{bigSum}}</h3>
		<h3>我在{{school}},学习{{subject}}</h3>
		<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>
	import {mapState,mapGetters} from 'vuex'
	export default {
		name:'Count',
		data() {
			return {
				n:1, //用户选择的数字
			}
		},
		computed:{
			//靠程序员自己亲自去写计算属性
			/* sum(){
				return this.$store.state.sum
			},
			school(){
				return this.$store.state.school
			},
			subject(){
				return this.$store.state.subject
			}, */

			//借助mapState生成计算属性,从state中读取数据。(对象写法)
			// ...mapState({he:'sum',xuexiao:'school',xueke:'subject'}),

			//借助mapState生成计算属性,从state中读取数据。(数组写法)
			...mapState(['sum','school','subject']),

			/* ******************************************************************** */

			/* bigSum(){
				return this.$store.getters.bigSum
			}, */

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

		},
		methods: {
			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() {
			const x = mapState({he:'sum',xuexiao:'school',xueke:'subject'})
			console.log(x)
		},
	}
</script>

<style lang="css">
	button{
		margin-left: 5px;
	}
</style>

src/store/index.js

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

//准备actions------用于响应组件中的动作
const actions = {
	/* jia(context,value){
		console.log('actions中的jia被调用了')
		context.commit('JIA',value)
	},
	jian(context,value){
		console.log('actions中的jian被调用了')
		context.commit('JIAN',value)
	}, */
	jiaOdd(context,value){
		console.log('actions中的jiaOdd被调用了')
		if(context.state.sum % 2){
			context.commit('JIA',value)
		}
	},
	jiaWait(context,value){
		console.log('actions中的jiaWait被调用了')
		setTimeout(()=>{
			context.commit('JIA',value)
		},500)
	}
}
//准备mutations------用于操作数据(state)
const mutations = {
	JIA(state,value){
		console.log('mutations中的JIA被调用了')
		state.sum += value
	},
	JIAN(state,value){
		console.log('mutations中的JIAN被调用了')
		state.sum -= value
	}
}
//准备state------用于存储数据
const state = {
	sum:0, //当前的和
	school:'尚硅谷',
	subject:'前端'
}
//准备getters------用于将state中的数据进行加工
const getters = {
	bigSum(state){
		return state.sum*10
	}
}

//创建并暴露store
export default new Vuex.Store({
	actions,
	mutations,
	state,
	getters
})

6 mapActions与mapMutations

6.1 总结

6.2 细节

我们需要优化的是方法,如下:

首先使用mapMutations修改,如下:

但当点击+1之后,发生如下错误:

我们让JIA输出value看看什么情况,如下:

可以看到,这个value是一个事件,如下:

可以看到,mapMutations生成的函数,需要传递参数。

可以看到,我们调用函数时,并没有传递参数,所以默认传的参数是event,如下:

因此在调用函数时,要传入参数,如下:

如果不想在函数里面传参,还可以用下面这个方法(但我觉得复杂了):

我们采取函数传参的方法,此时mapMutations还可以使用数组方法,并使用简写形式,如下:

此时调用函数处为:

mapActions同理,不再过多说明,直接看源代码。

6.3 源代码

在求和案例vuex版基础上,要修改的代码有:
src/components/Count.vue

javascript 复制代码
<template>
	<div>
		<h1>当前求和为:{{sum}}</h1>
		<h3>当前求和放大10倍为:{{bigSum}}</h3>
		<h3>我在{{school}},学习{{subject}}</h3>
		<select v-model.number="n">
			<option value="1">1</option>
			<option value="2">2</option>
			<option value="3">3</option>
		</select>
		<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 {
		name:'Count',
		data() {
			return {
				n:1, //用户选择的数字
			}
		},
		computed:{
			//借助mapState生成计算属性,从state中读取数据。(对象写法)
			// ...mapState({he:'sum',xuexiao:'school',xueke:'subject'}),

			//借助mapState生成计算属性,从state中读取数据。(数组写法)
			...mapState(['sum','school','subject']),

			/* ******************************************************************** */

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

		},
		methods: {
			//程序员亲自写方法
			/* increment(){
				this.$store.commit('JIA',this.n)
			},
			decrement(){
				this.$store.commit('JIAN',this.n)
			}, */

			//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
			...mapMutations({increment:'JIA',decrement:'JIAN'}),

			//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法)
			// ...mapMutations(['JIA','JIAN']),

			/* ************************************************* */

			//程序员亲自写方法
			/* incrementOdd(){
				this.$store.dispatch('jiaOdd',this.n)
			},
			incrementWait(){
				this.$store.dispatch('jiaWait',this.n)
			}, */

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

			//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(数组写法)
			// ...mapActions(['jiaOdd','jiaWait'])
		},
		mounted() {
			const x = mapState({he:'sum',xuexiao:'school',xueke:'subject'})
			console.log(x)
		},
	}
</script>

<style lang="css">
	button{
		margin-left: 5px;
	}
</style>

src/store/index.js

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

//准备actions------用于响应组件中的动作
const actions = {
	/* jia(context,value){
		console.log('actions中的jia被调用了')
		context.commit('JIA',value)
	},
	jian(context,value){
		console.log('actions中的jian被调用了')
		context.commit('JIAN',value)
	}, */
	jiaOdd(context,value){
		console.log('actions中的jiaOdd被调用了')
		if(context.state.sum % 2){
			context.commit('JIA',value)
		}
	},
	jiaWait(context,value){
		console.log('actions中的jiaWait被调用了')
		setTimeout(()=>{
			context.commit('JIA',value)
		},500)
	}
}
//准备mutations------用于操作数据(state)
const mutations = {
	JIA(state,value){
		console.log('mutations中的JIA被调用了')
		state.sum += value
	},
	JIAN(state,value){
		console.log('mutations中的JIAN被调用了')
		state.sum -= value
	}
}
//准备state------用于存储数据
const state = {
	sum:0, //当前的和
	school:'尚硅谷',
	subject:'前端'
}
//准备getters------用于将state中的数据进行加工
const getters = {
	bigSum(state){
		return state.sum*10
	}
}

//创建并暴露store
export default new Vuex.Store({
	actions,
	mutations,
	state,
	getters
})

7 多组件共享数据

7.1 细节

看如下需求:sum和persons可以同时共享

细节一

首先是创建Person组件,并在App引入。

紧接着在vuex添加person数据,如下:

紧接着在Person组件引入数据,如下:

  • 这里有两种写法,但我们使用红色框的写法,如果我们使用绿色框的写法,就避免了一个问题,不利于学习
  • 因此在Person组件不使用简写方式,而在Count组件使用简写方式,这样利于学习


细节二

现在看看PersonList是怎么进行共享的,先看Count组件,借助mapState读取。

而Person组件是利用计算属性读取的,如下:

sum属性也同理,可以去看看。

7.2 源代码

在求和案例vuex版基础上,要修改的代码有:
src/components/Count.vue

javascript 复制代码
<template>
	<div>
		<h1>当前求和为:{{sum}}</h1>
		<h3>当前求和放大10倍为:{{bigSum}}</h3>
		<h3>我在{{school}},学习{{subject}}</h3>
		<h3 style="color:red">Person组件的总人数是:{{personList.length}}</h3>
		<select v-model.number="n">
			<option value="1">1</option>
			<option value="2">2</option>
			<option value="3">3</option>
		</select>
		<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 {
		name:'Count',
		data() {
			return {
				n:1, //用户选择的数字
			}
		},
		computed:{
			//借助mapState生成计算属性,从state中读取数据。(数组写法)
			...mapState(['sum','school','subject','personList']),
			//借助mapGetters生成计算属性,从getters中读取数据。(数组写法)
			...mapGetters(['bigSum'])
		},
		methods: {
			//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
			...mapMutations({increment:'JIA',decrement:'JIAN'}),
			//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)
			...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
		},
		mounted() {
			// const x = mapState({he:'sum',xuexiao:'school',xueke:'subject'})
			// console.log(x)
		},
	}
</script>

<style lang="css">
	button{
		margin-left: 5px;
	}
</style>

src/components/Person.vue

javascript 复制代码
<template>
	<div>
		<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>
	import {nanoid} from 'nanoid'
	export default {
		name:'Person',
		data() {
			return {
				name:''
			}
		},
		computed:{
			personList(){
				return this.$store.state.personList
			},
			sum(){
				return this.$store.state.sum
			}
		},
		methods: {
			add(){
				const personObj = {id:nanoid(),name:this.name}
				this.$store.commit('ADD_PERSON',personObj)
				this.name = ''
			}
		},
	}
</script>

src/store/index.js

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

//准备actions------用于响应组件中的动作
const actions = {
	/* jia(context,value){
		console.log('actions中的jia被调用了')
		context.commit('JIA',value)
	},
	jian(context,value){
		console.log('actions中的jian被调用了')
		context.commit('JIAN',value)
	}, */
	jiaOdd(context,value){
		console.log('actions中的jiaOdd被调用了')
		if(context.state.sum % 2){
			context.commit('JIA',value)
		}
	},
	jiaWait(context,value){
		console.log('actions中的jiaWait被调用了')
		setTimeout(()=>{
			context.commit('JIA',value)
		},500)
	}
}
//准备mutations------用于操作数据(state)
const mutations = {
	JIA(state,value){
		console.log('mutations中的JIA被调用了')
		state.sum += value
	},
	JIAN(state,value){
		console.log('mutations中的JIAN被调用了')
		state.sum -= value
	},
	ADD_PERSON(state,value){
		console.log('mutations中的ADD_PERSON被调用了')
		state.personList.unshift(value)
	}
}
//准备state------用于存储数据
const state = {
	sum:0, //当前的和
	school:'尚硅谷',
	subject:'前端',
	personList:[
		{id:'001',name:'张三'}
	]
}
//准备getters------用于将state中的数据进行加工
const getters = {
	bigSum(state){
		return state.sum*10
	}
}

//创建并暴露store
export default new Vuex.Store({
	actions,
	mutations,
	state,
	getters
})

src/App.vue

javascript 复制代码
<template>
	<div>
		<Count/>
		<hr>
		<Person/>
	</div>
</template>

<script>
	import Count from './components/Count'
	import Person from './components/Person'

	export default {
		name:'App',
		components:{Count,Person},
		mounted() {
			// console.log('App',this)
		},
	}
</script>

8 vuex模块化 + namespace

8.1 总结

8.2 第一部分

细节问题

细节一 配置store

一开始我们是怎么配置store的,如下:

但现在有个问题,以mutation为例,假设是一个电商系统,我们已经实现了求和模块、人员模块,那还要继续写订单模块,商品模块等,这样mutation内容就很多了,很难维护,还有就是所有程序员都操作一个mutation,也容易造成git版本控制冲突。

因此分类整理,相当于分别管理求和的store和人员的store,如下:

紧接着使用配置,如下:

还可以使用简写形式,如下:

细节二 Count组件读取store

由于是初学者,暴露时不使用简写形式,便于学习,如下:

mapstate

第一种读取数据方式如下,会发现比较复杂,如下:

第二种就是利用mapstate的语法,先不管personList,如下:

但这样写会报错,因为没使用namespace,所以要加上,如下:当添加上namespace后,就可以了。

接下来考虑personList(也要加上namespace)如下:

mapmutation mapaction mapgetters

同理,如下:

8.3 第二部分

细节问题

细节一 Person组件读取store

之前Person组件不使用简写方式,就是因为在这里要学习不使用map方法时如何读取store。

读取state

可以看到,用如下的读取方式:

commit

不用简写形式调用Mutations时使用commit,在这里的语法形式如下:

增加一些功能

我们还需要练习getters和dispatch的调用方法,因此在actions和getters上添加一些功能。如下:

getters

在内容添加firstPersonName,如下:

因此需要使用计算属性,这里的写法很独特。

这里有一个方法,就是输出store看看它究竟是什么写法,如下:

所以应该这样写,如下:

dispatch

然后调用dispatch,和getters类似的写法,如下:

细节二 store配置管理问题

直接将person和count拆分管理,如下:

然后在index里面引入就行,如下:

细节三 发请求

在person中添加actions,这个api可以返回一个小语录,这个小语录作为名字,如下:

紧接着在person组件配置,如下:

8.4 源代码

在求和案例vuex版基础上,要修改的代码有:
src/components/Count.vue

javascript 复制代码
<template>
	<div>
		<h1>当前求和为:{{sum}}</h1>
		<h3>当前求和放大10倍为:{{bigSum}}</h3>
		<h3>我在{{school}},学习{{subject}}</h3>
		<h3 style="color:red">Person组件的总人数是:{{personList.length}}</h3>
		<select v-model.number="n">
			<option value="1">1</option>
			<option value="2">2</option>
			<option value="3">3</option>
		</select>
		<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 {
		name:'Count',
		data() {
			return {
				n:1, //用户选择的数字
			}
		},
		computed:{
			//借助mapState生成计算属性,从state中读取数据。(数组写法)
			...mapState('countAbout',['sum','school','subject']),
			...mapState('personAbout',['personList']),
			//借助mapGetters生成计算属性,从getters中读取数据。(数组写法)
			...mapGetters('countAbout',['bigSum'])
		},
		methods: {
			//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
			...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
			//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)
			...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
		},
		mounted() {
			console.log(this.$store)
		},
	}
</script>

<style lang="css">
	button{
		margin-left: 5px;
	}
</style>

src/components/Person.vue

javascript 复制代码
<template>
	<div>
		<h1>人员列表</h1>
		<h3 style="color:red">Count组件求和为:{{sum}}</h3>
		<h3>列表中第一个人的名字是:{{firstPersonName}}</h3>
		<input type="text" placeholder="请输入名字" v-model="name">
		<button @click="add">添加</button>
		<button @click="addWang">添加一个姓王的人</button>
		<button @click="addPersonServer">添加一个人,名字随机</button>
		<ul>
			<li v-for="p in personList" :key="p.id">{{p.name}}</li>
		</ul>
	</div>
</template>

<script>
	import {nanoid} from 'nanoid'
	export default {
		name:'Person',
		data() {
			return {
				name:''
			}
		},
		computed:{
			personList(){
				return this.$store.state.personAbout.personList
			},
			sum(){
				return this.$store.state.countAbout.sum
			},
			firstPersonName(){
				return this.$store.getters['personAbout/firstPersonName']
			}
		},
		methods: {
			add(){
				const personObj = {id:nanoid(),name:this.name}
				this.$store.commit('personAbout/ADD_PERSON',personObj)
				this.name = ''
			},
			addWang(){
				const personObj = {id:nanoid(),name:this.name}
				this.$store.dispatch('personAbout/addPersonWang',personObj)
				this.name = ''
			},
			addPersonServer(){
				this.$store.dispatch('personAbout/addPersonServer')
			}
		},
	}
</script>

src/store/count.js

javascript 复制代码
//求和相关的配置
export default {
	namespaced:true,
	actions:{
		jiaOdd(context,value){
			console.log('actions中的jiaOdd被调用了')
			if(context.state.sum % 2){
				context.commit('JIA',value)
			}
		},
		jiaWait(context,value){
			console.log('actions中的jiaWait被调用了')
			setTimeout(()=>{
				context.commit('JIA',value)
			},500)
		}
	},
	mutations:{
		JIA(state,value){
			console.log('mutations中的JIA被调用了')
			state.sum += value
		},
		JIAN(state,value){
			console.log('mutations中的JIAN被调用了')
			state.sum -= value
		},
	},
	state:{
		sum:0, //当前的和
		school:'尚硅谷',
		subject:'前端',
	},
	getters:{
		bigSum(state){
			return state.sum*10
		}
	},
}

src/store/index.js

javascript 复制代码
//该文件用于创建Vuex中最为核心的store
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
import countOptions from './count'
import personOptions from './person'
//应用Vuex插件
Vue.use(Vuex)

//创建并暴露store
export default new Vuex.Store({
	modules:{
		countAbout:countOptions,
		personAbout:personOptions
	}
})

src/store/person.js

javascript 复制代码
//人员管理相关的配置
import axios from 'axios'
import { nanoid } from 'nanoid'
export default {
	namespaced:true,
	actions:{
		addPersonWang(context,value){
			if(value.name.indexOf('王') === 0){
				context.commit('ADD_PERSON',value)
			}else{
				alert('添加的人必须姓王!')
			}
		},
		addPersonServer(context){
			axios.get('https://api.uixsj.cn/hitokoto/get?type=social').then(
				response => {
					context.commit('ADD_PERSON',{id:nanoid(),name:response.data})
				},
				error => {
					alert(error.message)
				}
			)
		}
	},
	mutations:{
		ADD_PERSON(state,value){
			console.log('mutations中的ADD_PERSON被调用了')
			state.personList.unshift(value)
		}
	},
	state:{
		personList:[
			{id:'001',name:'张三'}
		]
	},
	getters:{
		firstPersonName(state){
			return state.personList[0].name
		}
	},
}

src/App.vue

javascript 复制代码
<template>
	<div>
		<Count/>
		<hr>
		<Person/>
	</div>
</template>

<script>
	import Count from './components/Count'
	import Person from './components/Person'

	export default {
		name:'App',
		components:{Count,Person},
		mounted() {
			// console.log('App',this)
		},
	}
</script>
相关推荐
zoe_ya1 分钟前
react-diff-viewer 如何实现语法高亮
javascript·react.js·ecmascript
Clockwiseee12 分钟前
RCE联系
数据库·redis·缓存·web
大G哥12 分钟前
项目中利用webpack的require.context实现批量引入/导入图片
前端·webpack·node.js
sunbyte14 分钟前
Three.js + React 实战系列 - 联系方式提交表单区域 Contact 组件✨(表单绑定 + 表单验证)
开发语言·javascript·react.js
有事没事实验室27 分钟前
CSS 盒子模型与元素定位
前端·css·开源·html5
(ღ星辰ღ)29 分钟前
js应用opencv
开发语言·javascript·opencv
浩~~35 分钟前
HTML5 中实现盒子水平垂直居中的方法
java·服务器·前端
互联网搬砖老肖40 分钟前
Web 架构之故障自愈方案
前端·架构·github
天上掉下来个程小白44 分钟前
添加购物车-02.代码开发
java·服务器·前端·后端·spring·微信小程序·苍穹外卖
网络空间小黑1 小时前
WEB渗透测试----信息收集
服务器·前端·网络·安全·web安全·网络安全