[TOC]
## **1. 为什么要模块化**
* 由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
* 为了解决以上问题,Vuex 允许我们将 store 分割成**模块(module)**。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
## **2. 模块入门**
### **2.1 单文件多module**
用vue create 创建的简单demo
1. `main.js`
~~~
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
Vue.config.productionTip = false;
new Vue({
router,
store,
render: h => h(App)
}).$mount("#app");
~~~
2. `store/index.js`
~~~
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
const moduleA = {
state: () => ({
conut: 11,
message: '我是A模块'
}),
mutations: {},
actions: {},
getters: {}
}
const moduleB = {
state: () => ({
message: '我来自B模块-_-',
}),
mutations: {},
actions: {}
}
export default new Vuex.Store({
state: {},
mutations: {},
actions: {},
modules: { //modules中添加模块
a: moduleA,
b: moduleB
}
});
~~~
3. `about.js`
~~~
<template>
<div class="about">
<h1>This is an about page</h1>
<br>
<h2>
{{this.$store.state.a.message}}
</h2>
<br>
<h2>
{{this.$store.state.b.message}}
</h2>
</div>
</template>
~~~
![](https://img.kancloud.cn/06/78/06781f19972a71855914ec46fbc70faf_763x406.png)
**注意:模块中mutation、getters和actions接收的参数是本地化的state,即本身的数据**
### **2.2 一个module一个文件**
上边的例子显然不符合vuex木块化的初衷,store/index.js文件还是很bloated,所以我们要把模块拆出来,成单个的module文件
例如:将a和b模块拆出来,对应store下边连个目录
![](https://img.kancloud.cn/48/2f/482f5d1f56a427314d4c54421760be02_272x126.png)
1. `store/moduleA/index.js`
~~~
export const moduleA = {
state: () => ({
conut: 11,
message: '我是A模块'
}),
mutations: {},
actions: {},
getters: {}
}
~~~
2. `store/moduleB/index.js`
~~~
export const moduleB = {
state: () => ({
message: '我来自B模块-_-',
}),
mutations: {},
actions: {}
}
~~~
3. store/index.js
~~~
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
import {moduleA} from './moduleA'
import {moduleB} from './moduleB'
export default new Vuex.Store({
state: {},
mutations: {},
actions: {},
modules: {
a: moduleA,
b: moduleB
}
});
~~~
**`import {moduleA} from './moduleA'` 默认加载moduleA目录下的index.js文件
如果有其他文件,直接加文件名即可,例如`import {moduleA} from './moduleA/test'` 加载moduleA目录下的test.js文件**
### **2.3 子模块获取根模块的state**
注意:在开启和未开始名称空间后,访问的区别,此时getters,actions和mutions都是注册到全局命名空间中的
1. 对于模块内部的 action,局部状态通过`context.state`暴露出来,根节点状态则为`context.rootState`:
2. 对于模块内部的 `getter`,根节点状态会作为第三个参数暴露出来:
#### 2.3.1 getters访问
1. `moduleA/index.js`
~~~
getters: {
rootMessages(state, getters, rootState) {
console.log("huoquget");
return state.count + rootState.rootMessage
}
}
~~~
2. 访问方式
1)使用`this.$store.getters.rootMessages`直接访问
2)使用`mapGetters`辅助函数
~~~
<script>
import {mapGetters} from 'vuex' //引入辅助函数
export default {
computed: {
...mapGetters(['rootMessages'])
}
}
</script>
~~~
` ...mapGetters(['rootMessages']) `名称一致简写,否则
可可以写成别名的方式
~~~
computed: {
// ...mapGetters(['rootMessages']) 简写形式
...mapGetters({
roots: 'rootMessages' //别名:getter属性
})
}
~~~
全js
~~~
export const moduleA = {
state: () => ({
count: 11,
message: '我是A模块'
}),
mutations: {},
actions: {
rootAction({state, rootState}) {
console.log("根Message:" + rootState.message + "mokuaiA" + state)
},
},
getters: {
rootMessages(state, getters, rootState) {
console.log("huoquget");
return state.count + rootState.rootMessage
}
}
}
~~~
about.js
~~~
<template>
<div class="about">
<h1>This is an about page</h1>
<br>
<h2>
{{this.$store.state.a.message}}
</h2>
<br>
<h2>
{{this.$store.state.b.message}}
</h2>
<h1>
<!-- this. 去掉也可以,访问getter1 -->
访问子模块:{{this.$store.getters.rootMessages}}
</h1>
<br>
访问方法二:{{rootMessages}}
<!-- 访问方法二起别名:{{roots}} -->
</div>
</template>
<script>
import {mapGetters} from 'vuex'
export default {
computed: {
...mapGetters(['rootMessages']) //简写形式
// ...mapGetters({ //别名方式
// roots: 'rootMessages' //别名:getter属性
// })
}
}
</script>
~~~
由此可以看出,getters是注册到全局空间当中了
## 3. 模块的名称空间
### 3.1 案例引出
1. store/index.js
~~~
actions: {
saySomething({state,rootState}) {
console.log("我是根模块===》根Message:" + state.rootMessage)
},
},
~~~
2. moduleA/index.js
~~~
actions: {
saySomething({state}) {
console.log("我是子模块===》根Message:" + rootState.rootMessage + "-子模块值" + state.count)
},
~~~
根模块和子模块A都有一个action方法 saySomething
about.vue
~~~
<button @click="submit">说话</button>
<script>
import {mapGetters, mapActions} from 'vuex'
export default {
methods: {
...mapActions(['saySomething']),
submit() {
this.saySomething();
}
}
}
</script>
~~~
如下图:当点击按钮时,发现两个方法都被触发了,这就是模块化后,子module与父module都在一个名称空间带来的问题。
![](https://img.kancloud.cn/bb/2f/bb2f131fe40b9b237c49ec7109c9d8b0_828x300.png)
### **3.2 引入名称空间**
* 默认情况下,模块内部的 action、mutation 和 getter 是注册在**全局命名空间**的——这样使得多个模块能够对同一 mutation 或 action 作出响应。
* 如果希望你的模块具有更高的封装度和复用性,你可以通过添加`namespaced: true`的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。例如上边的问题,可以通过增加名称空间来解决
模块A
![](https://img.kancloud.cn/a6/57/a6576bd3984aea666783688fdbcf0cc5_434x264.png)
此时点击按钮,只会调用父模块的方法
![](https://img.kancloud.cn/e9/e4/e9e4a4133ffbd6b4d44669e260189efb_816x305.png)
#### **3.2.1 那如何调用子组件的action,mution和gettters呢?**
就是调用方法的时候 '模块名/属性名',如:
![](https://img.kancloud.cn/6d/4e/6d4e0e352701533991b5bf376ecebe8a_306x115.png)
1)调用actions
dispatch('a/saySomething')
2) 调用getter
getters['a/isAdmin']
3)调用mutations
commit('a/add')
完整
模块A
~~~
export const moduleA = {
namespaced: true,
state: () => ({
count: 11,
message: '我是A模块'
}),
mutations: {
add(state){
console.log("mutations - add执行")
state.count++;
}
},
actions: {
saySomething() {
console.log("action - saySomething执行")
},
},
getters: {
rootMessages(state, getters, rootState) {
console.log("getter 执行");
return state.count + rootState.rootMessage
}
}
}
~~~
about.vue
~~~
<template>
<div class="about">
<h1>This is an about page</h1>
<br>
<h2>
模块一:{{this.$store.state.a.count}}
</h2>
<br>
<h2>
{{this.$store.state.b.message}}
</h2>
<h1>
<!-- this. 去掉也可以,访问getter1 -->
<!-- 访问子模块:{{this.$store.getters.rootMessages}}-->
访问子模块:{{this.$store.getters['a/rootMessages']}}
</h1>
<br>
访问方法二:{{rootMessages}}
<!-- 访问方法二起别名:{{roots}} -->
<button @click="submit">说话</button>
</div>
</template>
<script>
import {mapGetters, mapActions} from 'vuex'
export default {
computed: {
...mapGetters({rootMessages: 'a/rootMessages'}) //简写形式
// ...mapGetters({ //别名方式
// roots: 'rootMessages' //别名:getter属性
// })
},
methods: {
...mapActions(['saySomething']),
submit() {
// this.saySomething();
this.$store.dispatch('a/saySomething');
this.$store.commit('a/add');
this.$store.getters['a/rootMessages'];
}
}
}
</script>
~~~
点击按钮,触发
![](https://img.kancloud.cn/d5/c6/d5c62672a2b9bee30271fb41b14367e6_827x333.png)
#### 3.2.1 在带命名空间的模块内访问全局内容(Global Assets)
如果你希望使用全局 state 和 getter,`rootState`和`rootGetters`会作为第三和第四参数传入 getter,也会通过`context`对象的属性传入 action。
~~~
modules: {
foo: {
namespaced: true,
getters: {
// 在这个模块的 getter 中,`getters` 被局部化了
// 你可以使用 getter 的第四个参数来调用 `rootGetters`
someGetter (state, getters, rootState, rootGetters) {
getters.someOtherGetter // -> 'foo/someOtherGetter'
rootGetters.someOtherGetter // -> 'someOtherGetter'
},
someOtherGetter: state => { ... }
},
actions: {
// 在这个模块中, dispatch 和 commit 也被局部化了
// 他们可以接受 `root` 属性以访问根 dispatch 或 commit
someAction ({ dispatch, commit, getters, rootGetters }) {
getters.someGetter // -> 'foo/someGetter'
rootGetters.someGetter // -> 'someGetter'
dispatch('someOtherAction') // -> 'foo/someOtherAction'
dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction'
commit('someMutation') // -> 'foo/someMutation'
commit('someMutation', null, { root: true }) // -> 'someMutation'
},
someOtherAction (ctx, payload) { ... }
}
}
}
~~~
#### 在带命名空间的模块注册全局 action
若需要在带命名空间的模块注册全局 action,你可添加`root: true`,并将这个 action 的定义放在函数`handler`中。例如:
~~~
{
actions: {
someOtherAction ({dispatch}) {
dispatch('someAction')
}
},
modules: {
foo: {
namespaced: true,
actions: {
someAction: {
root: true,
handler (namespacedContext, payload) { ... } // -> 'someAction'
}
}
}
}
}
~~~
#### **3.2.3 带命名空间的绑定函数**
当使用`mapState`,`mapGetters`,`mapActions`和`mapMutations`这些函数来绑定带命名空间的模块时,写起来可能比较繁琐:
~~~
computed: {
...mapState({
a: state => state.some.nested.module.a,
b: state => state.some.nested.module.b
})
},
methods: {
...mapActions([
'some/nested/module/foo', // -> this['some/nested/module/foo']()
'some/nested/module/bar' // -> this['some/nested/module/bar']()
])
}
~~~
对于这种情况,你可以将模块的空间名称字符串作为第一个参数传递给上述函数,这样所有绑定都会自动将该模块作为上下文。于是上面的例子可以简化为:
~~~
computed: {
...mapState('some/nested/module', {
a: state => state.a,
b: state => state.b
})
},
methods: {
...mapActions('some/nested/module', [
'foo', // -> this.foo()
'bar' // -> this.bar()
])
}
~~~
### **3.3 `createNamespacedHelpers`**
### 3.3.1 子组件中使用模块
* 创建基于某个命名空间辅助函数。它返回一个对象,对象里有新的绑定在给定命名空间值上的组件绑定辅助函数:
* 上边调用actons,mutations和getters、state都需要路径,有了createNamespacedHelpers工具,可以帮我们自动创建名称空间,引入空间的mapGetters、mapState、mapMutations和mapActions辅助函数
B模块
~~~
export const moduleB = {
namespaced: true, //开启名称空间
state: () => ({
message: '我来自B模块-_-',
count: 100
}),
mutations: {
add(state){
console.log("mutations B模块 - add执行")
state.count++;
}
},
actions: {
saySomething() {
console.log("action B模块- saySomething执行")
},
},
getters: {
rootMessages(state, getters, rootState) {
console.log("getter B模块 执行");
return state.count + rootState.rootMessage
}
}
}
~~~
`home.vue`
~~~
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png"/>
<h1>B的count:{{count}}</h1>
<button @click="submit">B模块说话</button>
</div>
</template>
<script>
import {createNamespacedHelpers} from 'vuex'
const {mapState, mapActions,mapMutations} = createNamespacedHelpers('b')
export default {
name: "Home",
components: {},
computed: {
...mapState(['count', 'message'])
},
methods: {
...mapActions(['saySomething']),
...mapMutations(['add']),
submit() {
this.saySomething();
this.add();
}
}
};
</script>
~~~
注意;createNamespacedHelpers('b')中的b,代表的是父木块中注册的子模块
![](https://img.kancloud.cn/2c/22/2c22f62842c854798bbe3f64e2d3a141_513x296.png)
点击按钮,count加1,action之类的都执行了
![](https://img.kancloud.cn/79/5e/795ea71a133118acd26ca864fc25a3e9_841x421.png)
#### 3.3.2 引入多个名称空间
例如在上边的例子中,再引入A模块(但是要起别名)
home.vue
~~~
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png"/>
<h1>B的count:{{count}}</h1>
<h1>A的count:{{countA}}</h1>
<button @click="submit">B模块说话</button>
</div>
</template>
<script>
import {createNamespacedHelpers} from 'vuex'
const {mapState, mapActions,mapMutations} = createNamespacedHelpers('b')
const {mapState: mapStateA,mapMutations: mapMutationsA} = createNamespacedHelpers('a')
export default {
name: "Home",
components: {},
computed: {
...mapState(['count', 'message']),
...mapStateA({countA: 'count'}) //A模块别名
},
methods: {
...mapActions(['saySomething']),
...mapMutations(['add']),
...mapMutationsA({addA:'add'}), //A模块别名
submit() {
this.saySomething();
this.add();
this.addA(); //调用A模块
}
}
};
</script>
~~~
home页
![](https://img.kancloud.cn/24/23/2423b1f99a7b9b41a669d207d550f775_770x351.png)
about页
![](https://img.kancloud.cn/dd/23/dd2325a6cc313bea6010455873b902e5_916x378.png)
由上可以看出数据两个页同步了
- vue
- 为什么要学vue
- 数据双向绑定
- vue指令
- v-bind创建HTML节点属性
- v-on绑定事件
- v-cloak
- v-text
- v-for和key属性
- v-if和v-show
- 案例1
- 自定义指令
- vue样式
- vue生命周期
- vue过滤器
- 自定义键盘修饰符
- 跨域请求
- vue组件
- 组件基础
- 引入vue文件组件
- 引入render函数作为组件
- 兄弟间组件通信
- 组件函数数据传递练习
- 路由
- 数据监听
- webpack
- vue校验
- vue笔记
- form表单中input前部分默认输入,切不可修改
- mixins
- 部署到nginx
- scope
- render
- 下载文件
- vue动态组件
- axios
- Promise
- vue进阶
- node-vue-webpack搭建
- vue事件
- 插槽
- vuex
- vuex基础
- vuex命名空间
- HTML递归?
- this.$nextTick异步更新dom
- elementui
- table
- 修改element ui样式
- form
- 优质博客
- vuex state数据与form元素绑定
- es6
- Promise