1 模块module
vuex由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割。
一个简单的案例如下:
在store文件夹下新建modules文件夹,在modules文件夹下新建moduleA.js和moduleB.js文件
|-store
|-index.js
|-modules
| |-moduleA.js
| |-moduleB.js
复制代码
moduleA..js代码如下:
const moduleA = {
state: {
name: 'people',
age: 30
},
mutations: {
addAge: state => {
state.age++
}
},
getters: {
manAge: state => {
return state.age + 10
}
},
actions: {
addAge: context => {
setTimeout(() => {
context.commit('addAge')
}, 3000);
}
}
}
export default moduleA
复制代码
moduleB.js代码如下:
const moduleB = {
state: {
name: 'dog',
age: 4
},
mutations: {
addAge: state => {
state.age++
}
},
getters: {
dogManAge: state => {
return state.age + 10
}
},
actions: {
addAge: context => {
setTimeout(() => {
context.commit('addAge')
}, 3000);
}
}
}
export default moduleB
复制代码
store下index.js文件代码如下:
import Vue from 'vue';
import Vuex from 'vuex';
import moduleA from './modules/moduleA';
import moduleB from './modules/moduleB';
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
export default store
复制代码
1.2 调用模块
在以上的代码中,我们通过模块的方法把store按照业务拆分成多个部分,这样使我们的代码结构更加清晰。那么问题来了,我们这样定义以后,我们该怎样使用模块中的变量呢?
以下全是没有设置命名空间时的调用方法。
1.2.1 state
获取sate需要带上模块的key才能获取。比如我们要获取moduleA中的name值,我们可以这样获取
computed: {
name: function () {
return this.$store.state.a.name
}
},
复制代码
1.2.2 getters
我们在A模块的getters中定义了manAge,在B模块的getters中定义了dogManAge。刚开始我以为获取getters跟获取state是一样的。因此我获取manAge的时候是这样写的。this.$store.getters.a.manAge,结果发现报错了。原来获取getters是直接获取的,并不用带模块名。
manAge: function () {
return this.$store.getters.manAge
},
复制代码
**虽然我们把stroe分成了模块A和B,但是getter是不分模块的。所以不能再A模块的getters定义了manAge,B模块的getters也定义manAge。这样子会命名重复的错误
1.2.3 mutations和actions
调用mutations和actions中定义的方法也是不分模块的,直接按全局调用就可以了。比如我在A模块定义为了addAge方法,在B模块也定义了addAge方法。
调用的时候直接这样调用就可以了。
methods: {
increment: function () {
this.$store.commit('addAge')
},
increment2: function () {
this.$store.dispatch('addAge')
}
},
复制代码
这里其实有个问题的,我在A和B模块都定义了addAge方法,当我在组件里调用this.$store.commit('addAge')的时候,A模块和B模块的方法都会被调用。这样是很危险的,可能你不想调用B模块的addAge方法,结果也被调用了。这里自己要检查情况。
其实官方文档有这样一句话,我直接竟然没看到。默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。
1.2.4 局部状态
没有使用命名空间的时候,对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象。
mutations: {
addAge: state => {
// 这里的state就是moduleA中的state。跟其他模块中的state没有关系
state.age++
}
},
getters: {
// 这里的state就是moduleA中的state。跟其他模块中的state没有关系
manAge: state => {
return state.age + 10
}
},
复制代码
同样,对于模块内部的 actions,局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState
actions: {
addAge: context => {
// context.state 就是moduleA中的state,context.rootState就是全局state
console.log('context.state:', context.state) // {age:32,name:'people'}
console.log('context.rootState:', context.rootState) //{a:{age:32,name:'people'},b:{age:6,name='dog'}}
setTimeout(() => {
context.commit('addAge')
}, 3000);
}
}
复制代码
1.3 命名空间
首先命名空间是啥?这个我还在自己年幼的时候也不懂,而且这个名字看起来就能奇怪。命名空间就是给各个变量或者方法的调用添加前缀。比如我们有个方法xxx。我们不加命名空间的时候调用就是xxx()。我们如果给它加一个命名空间a,那么我们调用的时候就是a.xxx()。我们给她加个命名空间b,我们调用就会b.xxx()
在store中添加命名空间很简单。我们只需要增加一个nameSpaced为true的字段就可以。命名空间名就是我们在index.js中注册时的key值。比如我们的A模块,添加命名空间后代码如下。
const moduleA = {
namespaced: true,
state: {
name: 'people',
age: 30
},
mutations: {
addAge: state => {
// 这里的state就是moduleA中的state。跟其他模块中的state没有关系
state.age++
}
},
getters: {
// 这里的state就是moduleA中的state。跟其他模块中的state没有关系
manAge: state => {
return state.age + 10
}
},
actions: {
addAge: context => {
// context.state 就是moduleA中的state,context.rootState就是全局state
console.log('context.state:', context.state) // {age:32,name:'people'}
console.log('context.rootState:', context.rootState) //{a:{age:32,name:'people'},b:{age:6,name='dog'}}
setTimeout(() => {
context.commit('addAge')
}, 3000);
}
}
}
export default moduleA
复制代码
1.3.1 state
添加了命名空间后我们获取state跟之前的方法一样
age: function () {
return this.$store.state.a.age
},
复制代码
1.3.2 getters,mutations和actions
调用getters,mutations和actions就要加命名空间了。
// getters
manAge: function () {
return this.$store.getters['a/manAge']
}
复制代码
mutaions和actions
methods: {
increment: function () {
this.$store.commit('a/addAge')
},
increment2: function () {
this.$store.dispatch('a/addAge')
}
},
复制代码
1.3.3 局部状态
添加上命名空间后,rootState 和 rootGetter 会作为第三和第四参数传入 getter,也会通过 context 对象的属性传入 action
getters: {
// 这里的state就是moduleA中的state。跟其他模块中的state没有关系
manAge: (state, getters, rootState, rootGetters) => {
console.log('getters:', getters) // {manAge:40}
console.log('rootState:', rootState) // {a:{age:30,name:'people'},b:{age:4,name:'dog'}}
console.log('rootGetters:', rootGetters) // {'a/manAge':40,dogManAge:14}
return state.age + 10
}
},
复制代码
1.3.4 带命名空间注册全局action
我们的action有时候要全局注册,可以把方法的root设置为true,并且改变方法的定义方式
A模块的addAge之前是这个样的
actions: {
addAge: context => {
setTimeout(() => {
context.commit('addAge')
}, 3000);
}
}
复制代码
我们改成这个样
actions: {
addAge: {
root: true,
handler: (context) => {
setTimeout(() => {
context.commit('addAge')
}, 3000);
}
}
}
复制代码
1.4 map函数
模块不加命名空间的时候,我们是不能使用store提供的map函数的,比如mapState,mapGetters,mapMutations和mapActions。加上命名空间后我们就可以使用了。
调用的时候第一个参数传命名空间,第一个传数组或者对象,就是我们要获取的属性
...mapState('a', [
'name',
'age'
]),
...mapGetters('a', [
'manAge'
]),
...mapState('b', {
dogAge: 'age'
}),
...mapGetters('b', [
'dogManAge'
])
复制代码
1.5 createNamespacedHelpers`
我们也可以通过使用 createNamespacedHelpers 创建基于某个命名空间辅助函数。它返回一个对象,对象里有新的绑定在给定命名空间值上的组件绑定辅助函数。
使用方法如下:
import { createNamespacedHelpers } from 'vuex'
const { mapState: AmapState, mapGetters: AmapGetters } = createNamespacedHelpers('a')
const { mapState: BmapState, mapGetters: BmapGetters } = createNamespacedHelpers('b')
export default {
name: 'HelloWorld',
data() {
return {
count: 10
}
},
computed: {
...AmapState([
'name',
'age'
]),
...AmapGetters([
'manAge'
]),
...BmapState({
dogAge: 'age'
}),
...BmapGetters([
'dogManAge'
])
}
}
复制代码
1.5 其他
1.5.1 嵌套
store中模块是可以多层嵌套的。比如我们A模块嵌套一个C模块
const moduleA = {
namespaced: true,
state: {
name: 'people',
age: 30
},
mutations: {
addAge: state => {
state.age++
}
},
getters: {
manAge: (state) => {
return state.age + 10
}
},
actions: {
addAge: {
root: true,
handler: (context) => {
setTimeout(() => {
context.commit('addAge')
}, 3000);
}
}
},
modules: {
c: {
state: 60,
mutations: {
},
getters: {
},
actions: {}
}
}
}
export default moduleA
复制代码
1.5.2 动态注册
模块是可以动态注册和删除的。注册的方法是store.registerModule(moduleName,module) 。删除的方法是store.unregisterModule(moduleName)
this.$store.registerModule('d',{
})
复制代码
**以上是store中关于模块的部分,还是蛮重要的。