第5章:vuex
第5章:vuex
- 1 求和案例 纯vue版
- 2 vuex工作原理图
- 3 vuex案例
- 3.1 搭建vuex环境
- 错误写法
- 正确写法
- 3.2 求和案例vuex版
- 细节分析
- 源代码
- 4 getters配置项
- 4.1 细节
- 4.2 源代码
- 5 mapState与mapGetters
- 5.1 总结
- 5.2 细节分析
- 5.3 源代码
- 6 mapActions与mapMutations
- 6.1 总结
- 6.2 细节
- 6.3 源代码
- 7 多组件共享数据
- 7.1 细节
- 7.2 源代码
- 8 vuex模块化 + namespace
- 8.1 总结
- 8.2 第一部分
- 细节问题
- 8.3 第二部分
- 细节问题
- 8.4 源代码
1 求和案例 纯vue版
这里只需要住一个问题,就是Count组件的v-model.number=“n”,如果没有number,这个n就是个字符串。
src/components/Count.vue
<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
<template>
<div>
<Count/>
</div>
</template>
<script>
import Count from './components/Count'
export default {
name:'App',
components:{Count},
}
</script>
src/main.js
//引入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
<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
//该文件用于创建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
<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
//引入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
<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
//该文件用于创建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
<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
//该文件用于创建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
<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
//该文件用于创建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
<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
<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
//该文件用于创建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
<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
<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
<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
//求和相关的配置
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
//该文件用于创建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
//人员管理相关的配置
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
<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>