穷不怪父,苦不责妻,方为真男人!
【23.Vuex中的模块化和命名空间
】
[可以去官网看看Vuex3文档](Module | Vuex (vuejs.org))
- 由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
- 为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
modules模板如下:
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
//创建并暴露store
export default new Vuex.Store({
// property 简写 (用在对象某个 property 的 key 和被传入的变量同名时)
modules: {
a:moduleA,
b:moduleB,
},
});
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
const modulesCount = {
getters: {
// 这里的 `state` 对象是modulesCount的局部状态
bigSum(state, getters,rootstate,rootgetters) {
console.log('getters第一个参数:',state);
console.log('getters第二个参数:',getters);
console.log('getters第三个参数:',rootstate);
console.log('getters第四个参数:',rootgetters);
return state.sum * 10;
},
},
}
打印结果展示:
const modulesPerson = {
state: {
personLists: [{ id: '001', name: '伍六七' }],
},
}
Person组件中:
computed: {
// 完整版代码
personLists() {
return this.$store.state.modulesPerson.personLists;
},
sum() {
return this.$store.state.modulesCount.sum;
},
// 简写
/* ...mapState('modulesPerson',['personLists']),
...mapState('modulesCount',[ 'sum']), */
},
-
1.【解释:
this.$store.state.modulesPerson.personLists
】:我们看到(1)中的第三个参数,可以发现,模块A和模块B都放在根节点的state中【rootstate】,然后,再去存放数据的模块中去寻找自己需要的数据。 -
2.【解释:
...mapState('modulesPerson',['personLists'])
】:传递的两个参数,第一个参数:哪一个模块A或B;第二个参数,store中state的映射。 -
(3)我们看一下,我们如何在组件Person中使用模块B【modulesPerson】中的mutations里方法
addPerson
const modulesPerson = {
mutations: {
// 添加人员信息
addPerson(state, value) {
console.log('mutations中的addPerson被调用了');
console.log('addPerson', value);
// array.unshift(A):把A放在array中的第一个;这样添加的数据是响应式的。
state.personLists.unshift(value);
},
},
}
Person组件中:
<input type="text" placeholder="请输入名字" v-model="iptName" />
<button @click="addPerson">添加</button>
import {nanoid} from 'nanoid';
export default {
name: 'Person',
data() {
return {
iptName: '',
};
},
methods: {
addPerson() {
const personObj = {id:nanoid(),name:this.iptName}
this.$store.commit('modulesPerson/addPerson', personObj)
// 然后清空input框
this.iptName=''
},
},
};
-
1.【解释:
this.$store.commit('modulesPerson/addPerson', personObj)
】:我们看到该代码中commit(‘第一个参数’,第二个参数),-
第一个参数:必须要带上路径,即你调用的方法是在哪一个模块的mutations中【开启命名空间后,即namespaced:true】。
【比如
commit('modulesPerson/addPerson', personObj)
:此处就是,调用模块modulesPerson中mutations里的addPerson方法。】 -
第二个参数:要传递的数据。
-
-
(4)我们看一下,我们如何在组件Count中使用模块A【modulesCount】中的actions里方法
jiaOdd
const modulesCount = {
actions: {
jiaOdd(context, value) {
console.log('actions中的jiaOdd被调用了');
// 这里可以访问到state里存储的数据 sum
if (context.state.sum % 2) {
context.commit('JIA', value);
}
},
},
}
Count组件中:
<button @click="incrementOdd(n)">当前求和为奇数再加</button>
import { mapGetters, mapState, mapMutations, mapActions } from 'vuex';
export default {
name: 'Count',
methods: {
//**********完整版写法************** */
incrementOdd(n) {
this.$store.dispatch('modulesCount/jiaOdd',n);
},
//**********借助mapActions方法****************
//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)
...mapActions('modulesCount',{ incrementOdd: 'jiaOdd'}),
//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(数组写法)
// ...mapActions(['jiaOdd'])
},
};
-
1.【解释:
...mapActions('modulesCount',{ incrementOdd: 'jiaOdd'}),
】:完整版代码如下incrementOdd(n) { this.$store.dispatch('modulesCount/jiaOdd',n); },
-
第一个参数:必须要带上路径,即你调用的方法是在哪一个模块的actions中【开启命名空间后,即namespaced:true】。
【比如
dispatch('modulesCount/jiaOdd',n)
:此处就是,调用模块modulesCount中actions里的jiaOdd方法。】 -
第二个参数:要传递的数据。
-
【补充1namespaced: true
】: 使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。
目前页面展示:
vuex模块化编码:
store/index.js
//该文件用于创建Vuex中最为核心的store
import Vue from 'vue';
//引入Vuex
import Vuex from 'vuex';
import modulesCount from "./count";
import modulesPerson from "./person";
//应用Vuex插件
Vue.use(Vuex);
//创建并暴露store
export default new Vuex.Store({
// property 简写 (用在对象某个 property 的 key 和被传入的变量同名时)
modules: {
modulesCount,
modulesPerson,
},
});
store/count.js
export default {
namespaced: true,
state: {
sum: 0, //当前的和
Sname: '伍六七',
Sjob: '理发师',
},
mutations: {
// 这里的 `state` 对象是modulesCount的局部状态
JIA(state, value) {
console.log('mutations中的JIA被调用了');
state.sum += value;
},
JIAN(state, value) {
console.log('mutations中的JIAN被调用了');
state.sum -= value;
},
},
actions: {
jiaOdd(context, value) {
console.log('actions中的jiaOdd被调用了');
// 这里可以访问到state里存储的数据 sum
if (context.state.sum % 2) {
context.commit('JIA', value);
}
},
jiaWait(context, value) {
console.log('actions中的jiaWait被调用了');
setTimeout(() => {
context.commit('JIA', value);
}, 500);
},
},
getters: {
// 这里的 `state` 对象是modulesCount的局部状态
bigSum(state, getters,rootstate) {
console.log('getters第一个参数:',state);
console.log('getters第二个参数:',getters);
console.log('getters第三个参数:',rootstate);
return state.sum * 10;
},
},
};
store/person.js
export default {
namespaced: true,
state: {
personLists: [{ id: '001', name: '伍六七' }],
},
mutations: {
// 添加人员信息
addPerson(state, value) {
console.log('mutations中的addPerson被调用了');
console.log('addPerson', value);
// array.unshift(A):把A放在array中的第一个;这样添加的数据是响应式的。
state.personLists.unshift(value);
},
},
actions: {},
getters: {},
};
App.vue
<template>
<div>
<Count/>
<br/>
<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>
Count.vue
<template>
<div>
<h1>当前求和为:{{ sum }}</h1>
<h3>当前求和放大10倍为:{{ bigSum }}</h3>
<h3>我叫{{ Sname }},是一名{{ Sjob }}</h3>
<h3 style="color: red">
Person组件的总人数是:{{ personLists.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 { mapGetters, mapState, mapMutations, mapActions } from 'vuex';
export default {
name: 'Count',
data() {
return {
n: 1, //用户选择的数字
};
},
computed: {
//借助mapGetters生成计算属性,从getters中读取数据。(数组写法)
...mapGetters('modulesCount', ['bigSum']),
//借助mapState生成计算属性,从state中读取数据。(数组写法)
...mapState('modulesCount', ['sum', 'Sjob', 'Sname']),
...mapState('modulesPerson', ['personLists']),
},
methods: {
//**********借助mapMutations方法****************
//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
...mapMutations('modulesCount', {
increment: 'JIA',
decrement: 'JIAN',
}),
//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法)
// ...mapMutations([ 'JIA', 'JIAN' ]),
//**********完整版写法************** */
incrementOdd(n) {
this.$store.dispatch('modulesCount/jiaOdd',n);
},
//**********借助mapMutations方法****************
//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)
...mapActions('modulesCount', {
// incrementOdd: 'jiaOdd',
incrementWait: 'jiaWait',
}),
//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(数组写法)
// ...mapActions(['jiaOdd','jiaWait'])
},
mounted() {
console.log('Count', this.$store);
},
};
</script>
<style scoped>
button {
margin-left: 10px;
}
select {
margin-left: 20px;
}
</style>
Person.vue
<template>
<div>
<h1>人员列表</h1>
<h3 style="color: red">Count组件求和为:{{ sum }}</h3>
<input type="text" placeholder="请输入名字" v-model="iptName" />
<button @click="addPerson">添加</button>
<ul>
<li v-for="p in personLists" :key="p.id">{{ p.name }}</li>
</ul>
</div>
</template>
<script>
// import { mapState } from 'vuex';
import {nanoid} from 'nanoid';
export default {
name: 'Person',
data() {
return {
iptName: '',
};
},
computed: {
// 完整版代码
personLists() {
return this.$store.state.modulesPerson.personLists;
},
sum() {
return this.$store.state.modulesCount.sum;
},
// 简写
/* ...mapState('modulesPerson',['personLists']),
...mapState('modulesCount',[ 'sum']), */
},
methods: {
addPerson() {
const personObj = {id:nanoid(),name:this.iptName}
this.$store.commit('modulesPerson/addPerson', personObj)
// 然后清空input框
this.iptName=''
},
},
};
</script>
我们添加1个需求:输入框中添加的名字,必须姓:‘鸡’,如图所示。
1.第一种写法:在Person.vue中
<button @click="addPersonJi">添加一个姓鸡的</button>
methods: {
addPersonJi() {
if (this.iptName.indexOf('鸡')===0) {
const personObj = { id: nanoid(), name: this.iptName };
this.$store.commit('modulesPerson/addPerson', personObj);
// 然后清空input框
this.iptName = '';
} else {
this.iptName = '';
alert('请输入姓鸡的名字')
}
},
},
2.第二种写法:在模块modulesPerson的mutations中
mutations: {
addPersonJi(state, value) {
console.log('mutations中的addPersonJi被调用了');
// array.unshift(A):把A放在array中的第一个;这样添加的数据是响应式的。
if (value.name.indexOf('鸡')===0) {
state.personLists.unshift(value);
// 然后清空input框
} else {
alert('请输入姓鸡的名字')
}
},
},
而Person.vue中
methods: {
addPersonJi() {
const personObj = { id: nanoid(), name: this.iptName };
this.$store.commit('modulesPerson/addPersonJi', personObj);
// 然后清空input框
this.iptName = '';
},
},
3.第三种写法:在模块modulesPerson的actions中,最好不要直接修改state里面的数据;最好是去调用commit方法,在mutations里修改state对的数据。
mutations: {
// 添加人员信息
addPerson(state, value) {
console.log('mutations中的addPerson被调用了');
console.log('addPerson', value);
// array.unshift(A):把A放在array中的第一个;这样添加的数据是响应式的。
state.personLists.unshift(value);
},
},
actions: {
addPersonJi(context, value) {
console.log('actions中的addPersonJi被调用了');
// array.unshift(A):把A放在array中的第一个;这样添加的数据是响应式的。
if (value.name.indexOf('鸡')===0) {
context.commit('addPerson',value)
} else {
alert('请输入姓鸡的名字')
}
},
},
而Person.vue中
methods: {
addPersonJi() {
const personObj = { id: nanoid(), name: this.iptName };
this.$store.dispatch('modulesPerson/addPersonJi', personObj);
// 然后清空input框
this.iptName = '';
},
},
我们添加1个按钮:利用axios.get()去调用后台api,去随机生成一句情话,如图所示。
情话文案
简介:随机生成一句情话文案
文档:http://txapi.cn/api_detail?id=1601207567931932672
请求方式:GET
调用地址:http://api.txapi.cn/v1/c/love_talk?token=Z1QljZOZiT4NTG
{
"code": 200,
"msg": "ok",
"data": {
"text": "当你真正爱上一个人,你会有一种很亲切的感觉,他让你觉的很舒服,你可以信任他、依靠他。他像是一个亲密的家人,甚至可以说,比一个家人更亲密,这是亲密加上一种温馨的感觉,就是亲爱的感觉。"
},
"time": 1670592587018
}
Person.vue
<button @click="addPersonRandom">随机生成一句情话</button>
methods: {
addPersonRandom(){
this.$store.dispatch('modulesPerson/addPersonRandom');
},
},
modulesPerson模块中
actions: {
addPersonRandom(context) {
axios.get('http://api.txapi.cn/v1/c/love_talk?token=Z1QljZOZiT4NTG').then(
// 获取数据成功
Response => {
const personRandom = {id:nanoid(),name:Response.data.data}
context.commit('addPerson',personRandom)
},
// 获取数据失败,打印失败原因
error => {console.log(error.message);}
);
},
},
mutations: {
// 添加人员信息
addPerson(state, value) {
console.log('mutations中的addPerson被调用了');
console.log('addPerson', value);
// array.unshift(A):把A放在array中的第一个;这样添加的数据是响应式的。
state.personLists.unshift(value);
},
},
整个vuex模块化的完整编码:
store/index.js
//该文件用于创建Vuex中最为核心的store
import Vue from 'vue';
//引入Vuex
import Vuex from 'vuex';
import modulesCount from "./count";
import modulesPerson from "./person";
//应用Vuex插件
Vue.use(Vuex);
//创建并暴露store
export default new Vuex.Store({
// property 简写 (用在对象某个 property 的 key 和被传入的变量同名时)
modules: {
modulesCount,
modulesPerson,
},
});
store/count.js
export default {
namespaced: true,//开启命名空间
state: {
sum: 0, //当前的和
Sname: '伍六七',
Sjob: '理发师',
},
mutations: {
// 这里的 `state` 对象是modulesCount的局部状态
JIA(state, value) {
console.log('mutations中的JIA被调用了');
state.sum += value;
},
JIAN(state, value) {
console.log('mutations中的JIAN被调用了');
state.sum -= value;
},
},
actions: {
jiaOdd(context, value) {
console.log('actions中的jiaOdd被调用了');
// 这里可以访问到state里存储的数据 sum
if (context.state.sum % 2) {
context.commit('JIA', value);
}
},
jiaWait(context, value) {
console.log('actions中的jiaWait被调用了');
setTimeout(() => {
context.commit('JIA', value);
}, 500);
},
},
getters: {
// 这里的 `state` 对象是modulesCount的局部状态
bigSum(state, getters,rootstate,rootgetters) {
console.log('getters第一个参数:',state);
console.log('getters第二个参数:',getters);
console.log('getters第三个参数:',rootstate);
console.log('getters第四个参数:',rootgetters);
return state.sum * 10;
},
},
};
store/person.js
//人员管理相关的配置
import axios from 'axios'
import { nanoid } from 'nanoid'
export default {
namespaced: true, //开启命名空间
state: {
personLists: [{ id: '001', name: '伍六七' }],
},
mutations: {
// 添加人员信息
addPerson(state, value) {
console.log('mutations中的addPerson被调用了');
console.log('addPerson', value);
// array.unshift(A):把A放在array中的第一个;这样添加的数据是响应式的。
state.personLists.unshift(value);
},
},
actions: {
addPersonJi(context, value) {
console.log('actions中的addPersonJi被调用了');
// array.unshift(A):把A放在array中的第一个;这样添加的数据是响应式的。
if (value.name.indexOf('鸡') === 0) {
context.commit('addPerson', value);
} else {
alert('请输入姓鸡的名字');
}
},
addPersonRandom(context) {
axios.get('http://api.txapi.cn/v1/c/love_talk?token=Z1QljZOZiT4NTG').then(
// 获取数据成功
Response => {
const personRandom = {id:nanoid(),name:Response.data.data}
context.commit('addPerson',personRandom)
},
// 获取数据失败,打印失败原因
error => {console.log(error.message);}
);
},
},
getters: {},
};
components/Count.vue
<template>
<div>
<h1>当前求和为:{{ sum }}</h1>
<h3>当前求和放大10倍为:{{ bigSum }}</h3>
<h3>我叫{{ Sname }},是一名{{ Sjob }}</h3>
<h3 style="color: red">
Person组件的总人数是:{{ personLists.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 { mapGetters, mapState, mapMutations, mapActions } from 'vuex';
export default {
name: 'Count',
data() {
return {
n: 1, //用户选择的数字
};
},
computed: {
//借助mapGetters生成计算属性,从getters中读取数据。(数组写法)
...mapGetters('modulesCount', ['bigSum']),
//借助mapState生成计算属性,从state中读取数据。(数组写法)
...mapState('modulesCount', ['sum', 'Sjob', 'Sname']),
...mapState('modulesPerson', ['personLists']),
},
methods: {
//**********借助mapMutations方法****************
//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
...mapMutations('modulesCount', {
increment: 'JIA',
decrement: 'JIAN',
}),
//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法)
// ...mapMutations([ 'JIA', 'JIAN' ]),
//**********完整版写法************** */
incrementOdd(n) {
this.$store.dispatch('modulesCount/jiaOdd',n);
},
//**********借助mapMutations方法****************
//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)
...mapActions('modulesCount', {
// incrementOdd: 'jiaOdd',
incrementWait: 'jiaWait',
}),
//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(数组写法)
// ...mapActions(['jiaOdd','jiaWait'])
},
mounted() {
console.log('Count', this.$store);
},
};
</script>
<style scoped>
button {
margin-left: 10px;
}
select {
margin-left: 20px;
}
</style>
components/Person.vue
<template>
<div>
<h1>人员列表</h1>
<h3 style="color: red">Count组件求和为:{{ sum }}</h3>
<input type="text" placeholder="请输入名字" v-model="iptName" />
<button @click="addPerson">添加</button>
<button @click="addPersonJi">添加一个姓鸡的</button>
<button @click="addPersonRandom">随机生成一句情话</button>
<ul>
<li v-for="p in personLists" :key="p.id">{{ p.name }}</li>
</ul>
</div>
</template>
<script>
// import { mapState } from 'vuex';
import { nanoid } from 'nanoid';
export default {
name: 'Person',
data() {
return {
iptName: '',
};
},
computed: {
// 完整版代码
personLists() {
return this.$store.state.modulesPerson.personLists;
},
sum() {
return this.$store.state.modulesCount.sum;
},
// 简写
/* ...mapState('modulesPerson',['personLists']),
...mapState('modulesCount',[ 'sum']), */
},
methods: {
addPerson() {
const personObj = { id: nanoid(), name: this.iptName };
this.$store.commit('modulesPerson/addPerson', personObj);
// 然后清空input框
this.iptName = '';
},
addPersonJi() {
const personObj = { id: nanoid(), name: this.iptName };
this.$store.dispatch('modulesPerson/addPersonJi', personObj);
// 然后清空input框
this.iptName = '';
},
addPersonRandom(){
this.$store.dispatch('modulesPerson/addPersonRandom');
},
},
};
</script>
App.vue
<template>
<div>
<Count/>
<br/>
<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>
main.js
//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//引入vue-resource插件
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),
// property 简写 (用在对象某个 property 的 key 和被传入的变量同名时)
// 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
store,
// 生命周期钩子beforeCreate中模板未解析,且this是vm
beforeCreate() {
// this:指的是vm
Vue.prototype.$bus = this //安装全局事件总线$bus
}
})
总结:模块化+命名空间
-
目的:让代码更好维护,让多种数据分类更加明确。
-
修改
store.js
const countAbout = { namespaced:true,//开启命名空间 state:{x:1}, mutations: { ... }, actions: { ... }, getters: { bigSum(state){ return state.sum * 10 } } } const personAbout = { namespaced:true,//开启命名空间 state:{ ... }, mutations: { ... }, actions: { ... } } const store = new Vuex.Store({ modules: { countAbout, personAbout } })
-
开启命名空间后,组件中读取state数据:
//方式一:自己直接读取 this.$store.state.personAbout.list //方式二:借助mapState读取: ...mapState('countAbout',['sum','school','subject']),
-
开启命名空间后,组件中读取getters数据:
//方式一:自己直接读取 this.$store.getters['personAbout/firstPersonName'] //方式二:借助mapGetters读取: ...mapGetters('countAbout',['bigSum'])
-
开启命名空间后,组件中调用dispatch
//方式一:自己直接dispatch this.$store.dispatch('personAbout/addPersonWang',person) //方式二:借助mapActions: ...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
-
开启命名空间后,组件中调用commit
//方式一:自己直接commit this.$store.commit('personAbout/ADD_PERSON',person) //方式二:借助mapMutations: ...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),