Vuex 应用状态管理
1.Vuex 解决的问题及使⽤场景
⼀、本节⼤纲
- 组件开发中头疼的的问题
- 状态是什么?
- Vuex 状态管理
- Vuex 使⽤场景
⼆、组件开发中头疼的的问题
在之前学习组件的时候,我们了解到了⼀些组件之间传递数据的⽅法,可以⽐较⽅便地实现⽗⼦组件之间的数据通信。或者通过路由实现参数传递也是组件之间数据通信的⼀种⽅式。
但是我们仍然会遇到⼀些问题:
- 数据⼀般都是在⽗组件进⾏加载,向⼦组件传递。当出现跨多层级数据传递的时候,还是相对麻烦。如果我们想实现⾮⽗⼦组件之间数据通信,如上图红线连接的组件所示,虽然也可以逐级传递,但是很麻烦。
- 举个例⼦:A、B、C 三个组件都需要根据应⽤的⽤户数据 U 改变其数据显示效果及显示内容,那么⽤户数据 U 应该定义在哪个组件⾥⾯呢?好像定义在哪⾥都不合适。
- 如果 A 组件改变了⽤户数据 U ,组件 B 和 C 的如何知道数据 U 发⽣了变化并作出响应,实现数据的状态 ⼀致性?
三、状态是什么?
我们通常把应⽤各组件、各模块之间的公共数据叫做“状态”。
- 在开发 web 应⽤时,常把⼀些各个模块都会⽤到的公共数据保存到 session ⾥⾯,这些公共数据就是状态。⽐如:⽤户登录状态信息。
- 我们在写⼀段代码的时候,常常把⼀些多段代码都使⽤的公共变量定义抽取出来,⽤于判断执⾏逻辑。这些公共变量也是状态。
状态数据有两个特点:⼀是数据的共享,⼆是数据的变更将导致影响的产⽣。状态由于其共享性,导致其⼀旦发⽣变化,就会对引⽤它的模块或组件产⽣影响。所以通常伴随着“状态”,还有“状态管理”。⽽ Vuex 就是⼀个实现组件之间状态共享和状态变更管理的 Vue 插件。
四、Vuex 状态管理
官⽅定义:Vuex 是⼀个专为 Vue.js 应⽤程序开发的状态管理模式。它采⽤集中式存储管理应⽤的所有组件的状态,并以相应的规则保证状态以⼀种可预测的⽅式发⽣变化。
- 状态集中式状态管理:简单地说就是将多个组件中的公共数据,单独抽取到⼀个公共对象内进⾏存 储,并且这个公共存储对象是⼀个单例。
- Vuex 另外⼀个显著的作⽤就是实现公共状态的响应式编程。公共状态数据 State 的变化导致引⽤该状态的视图组件 View 发⽣变化,View 视图的⽤户输可以触发 Actions 动作,Actions 动作⼜可以改变公共状态 State。
五、Vuex 使⽤场景
- Vue 的 SPA 单⻚⾯应⽤组件规模较⼤的时候,出现⼤量的组件之间有数据通信的时候。
- Vue 组件之间出现数据传递及响应式编程困难的时候。
- Vue 多组件依赖于同⼀个状态,或者不同视图⾏为需要改变同⼀状态的时候。
2.Vuex 的第⼀个例⼦
⼀、本节⼤纲
- Vuex 插件的安装
- 计数器组件基础代码
- Vuex 集中化存储
⼆、Vuex 插件的安装
在 Vue 项⽬⽬录下执⾏如下命令:
npm install vuex --save
安装完成之后,在 package.json 的项⽬配置⽂件中会显示出 vuex 的安装版本。⼿动在项⽬的 src ⽬录下创建 strore ⽂件夹和 index.js ⽂件,index.js ⽂件内容如下。该⽂件夹内的内容就是 Vuex 进⾏状态集中管理的“仓库”。
import Vue from "vue";
import Vuex from "vuex";
//使⽤Vuex插件
Vue.use(Vuex);
//注意这⾥创建的是store对象,不是vuex对象
const store = new Vuex.Store({
state: {},
mutations: {},
actions: {},
getters: {},
modules: {},
});
//导出对象
export default store;
从上图中可以看到,在 store 对象中我们定义了⼀系列的⼦对象 state、mutations、actions、getters、 modules ,这就是我们学习 Vuex 的主要内容。
我们建好了“仓库”,还得把它引⼊到项⽬⾥⾯来。在 main.js 中加⼊如下代码:
import store from "./store";
//将store加⼊vue实例
new Vue({
...store, //加这⾥
render: (h) => h(App),
}).$mount("#app");
这样 Vuex 就安装完成了,在项⽬⾥⾯就可以使⽤了。
三、计数器组件基础代码
我们定义两个组件、⼀个组件 name 为 VuexCpnOne,另⼀个组件 name 为 VuexCpnTwo。代码完全相同,如下:
<template>
<div>
<div>计数值:{{ counter }}</div>
<!-- 1. couter值不同步 -->
<button @click="counter++">+1</button>
<button @click="counter--">-1</button>
</div>
</template>
<script>
export default {
name: "VuexCpnOne",
data() {
return {
counter: 0,
};
},
};
</script>
<style></style>
我们定义的两个组件,除了名字不同,其他的代码完全⼀致。代码中定义了⼀个数据变量 counter。
然后我们将这两个组件引⼊到同⼀个⽗组件⾥⾯,也就是说这两个组件是兄弟组件。⽗组件 App.vue 的代码:
<template>
<div id="app">
<vuex-cpn-one></vuex-cpn-one>
<hr />
<vuex-cpn-two></vuex-cpn-two>
</div>
</template>
<script>
import VuexCpnOne from "./components/VuexCpnOne.vue";
import VuexCpnTwo from "./components/VuexCpnTwo.vue";
export default {
name: "App",
components: {
VuexCpnOne,
VuexCpnTwo,
},
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
最终的实现效果如下:
两个组件的 counter 的值不同步,因为 counter 是分别定义在两个组件⾥⾯的,从程序运⾏的⻆度来讲, counter 是⼀个局部变量。
如果我们希望点击组件 VuexCpnOne 的按钮,同步影响组件 VuexCpnTwo 的 counter 值;点击组件 VuexCpnTwo 的按钮,同步影响组件 VuexCpnOne 的 counter 值。该怎么做?
⽐较麻烦的做法就是:点击 VuexCpnOne 按钮,向⽗组件传递点击事件,⽗组件向 VuexCpnTwo 传递 counter 数据。还有⼀种简单的做法就是将 counter 变成⼀个全局变量。
四、Vuex 集中化存储
那么我们怎么让 counter 变成⼀个全局变量?答案就是使⽤ vuex,将 counter 定义在 store/index.js 中的 store 对象的 state 状态对象⾥⾯。
const store = new Vuex.Store({
state: {
counter: 0,
},
});
然后修改 VuexCpnOne 组件和 VuexCpnTwo 组件的代码如下:
<template>
<div>
<div>计数值:{{$store.state.counter}}</div>
<button @click="$store.state.counter++">+1</button>
<button @click="$store.state.counter--">-1</button>
</div>
</template>
<script>
export default {
name:'VuexCpnOne'
}
</script>
$store
代表项⽬全局的集中存储的 store 对象,即 store/index.js 中定义的 store 对象- store 对象中的 state 属性,⽤于定义全局变量,多个组件都可以使⽤该变量
实现效果:⽆论我们点击哪⼀个组件的加 1 减 1 按钮,counter 的值在两个组件⾥⾯的显示都是同步的⼀致的。由此我们可以知道$store.state.counter
状态是响应式的,即:我们点击组件 VuexCpnOne 的按钮,组件 VuexCpnTwo 中的 counter 也发⽣变化。
直接修改 state 变量值虽然可以做到组件的响应式编程,但这种做法仍然是不好的做法。
3.mutations 的使⽤与状态跟踪
⼀、本节⼤纲
- mutations 的作⽤
- mutations 的基本使⽤
- vue devtools 状态跟踪
- mutations ⽅法携带参数
- mutations 常量
⼆、mutations 的作⽤
在上⼀节我们通过⼀个简单的双组件 VuexCpnOne 和 VuexCpnTwo 的例⼦,使⽤ Vuex 实现了计数状态
- 当点击任何⼀个组件中的按钮,实际上调⽤了
$store.state.counter++
和$store.state.counter--
,就修改了公共状态 counter 计数的值,从⽽影响另⼀个组件的显示内容同步。 - 直接对$store.state 中的状态进⾏赋值操作,是可以实现响应式的,即:state 的变化影响所有引⽤ 它的视图的变化。
但是存在⼀个问题:我们没有办法进⾏状态跟踪,也就是我们只能看到状态的结果,⽆法知道状态改变的过程。⽐如: 我们想知道经过⼏次点击,counter 的值变成了 3,第⼀次点击按钮后 counter 的值是什么,第⼆次点击后 counter 的值是什么。
可能有⼈会说,这很好操作啊,点⼀下看⼀下变化就好了,但是我们只是两个组件引⽤了公共的 state 中的⼀个 counter 变量。如果很多个组件引⽤很多的公共状态 state,该如何跟踪每⼀次点击按钮操作之后的 state 变化?如何留下痕迹?
直接对 $store.state
中的状态进⾏赋值操作是⽆法留痕的,我们需要使⽤ mutation(改变)。
mutation 是实际上就是对 state 状态进⾏操作的⾃定义⽅法,通过触发 mutations ⽅法修改的 state 可以留痕,可以被 vue devtools 调试⼯具跟踪。
但是注意不要在 mutation ⾃定义⽅法⾥⾯进⾏异步操作,⽐如发送⽹络请求。因为异步回调⽆法有效的被 mutation 跟踪,所以mutation ⾃定义⽅法⾥⾯必须是同步操作。
三、mutations 的基本使⽤
在上⼀节双组件 VuexCpnOne 和 VuexCpnTwo 的例⼦的基础上进⾏代码修改,⾸先定义 mutations :
const store = new Vuex.Store({
...
state:{
counter: 0
},
mutations:{
add(state){
state.counter++
},
sub(state){
state.counter--
}
},
...
})
mutations ⾃定义⽅法的第⼀个参数是就是 state,我们可以通过 state 参数修改状态。然后在组件中使⽤ $store.commit()
⽅法触发 mutation ,commit 的第⼀个参数是 mutation 的⽅法名。如下:
<button @click="$store.commit('add')">+1</button>
<button @click="$store.commit('sub')">-1</button>
视图效果和使⽤ $store.state.counter++
和 $store.state.counter--
是⼀样的。但是使⽤ mutation 改变的 state 会留痕,可以被跟踪。
四、vue devtools 状态跟踪
vue devtools 是专⻔⽤于 vue 开发调试的⼯具,在各个浏览器中以扩展程序的形式出现,参考各浏览器安装扩展程序的⽅法⾃⾏安装⼀下。
使⽤⽅法:调出浏览器的开发者⼯具,可以看到 vue 选项。可以对组件、路由、vuex 等进⾏⽅便地查看和调试。
五、 mutations ⽅法携带参数
现在我们变化⼀下需求,希望每点击⼀次按钮加 5 减 5 或加 n 减 n,不再是加 1 减 1。这样我们就希望 mutation ⾃定义的⽅法能够传参,这样我们就能够针对 state 状态做更灵活的操作,适应更⼴泛的需求。
mutations:{
add(state,num){
state.counter = state.counter + num
},
sub(state,num){
state.counter = state.counter - num
}
},
双组件计数器的例⼦,我们触发 mutation 的时候,是这样的代码:
<button @click="$store.commit('add',5)">+5</button>
<button @click="$store.commit('sub',5)">-5</button>
mutations 除了 state 只能传递⼀个参数,这个参数有个专有名词叫做 payload。
只有⼀个参数,那我们希望传递多组数据的时候怎么办?可以将它们封装到⼀个对象⾥⾯。所以 payload 可以是⼀个基础的数据类型,也可以是⼀个对象。
payload 对象代码如下:
mutations:{
add(state,payload){
state.counter = state.counter + payload.num * payload.multiple
},
sub(state,payload){
state.counter = state.counter - payload.num * payload.multiple
}
},
通过为 mutation ⽅法传递 payload 对象,实现加减 5 的 2 倍,即加减 1
<button @click="$store.commit('add',{num:5,multiple:2})">+5</button>
<button @click="$store.commit('sub',{num:5,multiple:2})">-5</button>
六、mutations 常量
通过上⾯的例⼦,⼤家可能注意到:
- 那就是 mutation 函数⼀次定义,在多个组件内多次 commit 调⽤。
- 触发 mutation 的⽅法 commit 的第⼀个参数,就是 mutation 函数的名称。
那么,我们在开发中就有可能遇到⼀个问题:如果有开发⼈员修改了 mutation 函数的⽅法名,那么我们如何保证对应的 commit ⽅法的参数⼀也对应的进⾏修改?⽐较笨的⽅式就是字符串查找,然后⼀个⼀个 改。还有⼀种情况就是:通过查找的⽅式⼀个⼀个改,漏掉了怎么办?这种可能性很⼤。所以我们在⼀开始就要避免这个问题:将 mutation 函数名称定义为常量。新建⼀个⽂件叫做 store/mutation-types.js
定义常量
export const COUNTER_ADD = "add";
在 mutation 函数定义的时候,先导⼊ mutation-types,再使⽤ []
引⽤常量
import {
COUNTER_ADD
} from './mutation-types'
const store = new Vuex.Store({
state:{
counter: 0
},
mutations:{
[COUNTER_ADD] (state,payload){
state.counter = state.counter + payload.num * payload.multiple
},
...
}
})
在调⽤ commit 触发 mutation 的时候,同样先导⼊ mutation-types,再使⽤常量。注意通过 js 模块导⼊的 COUNTER_ADD 不能再 html ⾥⾯被使⽤,所以我们需要单独定义 method ,在 method 中使⽤常量。
import { COUNTER_ADD } from "../store/mutation-types";
export default {
name: "VuexCpnTwo",
methods: {
addCounter() {
this.$store.commit(COUNTER_ADD, { num: 5, multiple: 2 });
},
},
};
<button @click="addCounter()">+5</button>
4.全局计算属性 getters
⼀、本节⼤纲
- 英⽂ fullName 需求基础实现
- v-model 绑定 state 状态数据的标准做法
- Vuex 的 getters 的定义与使⽤
⼆、英⽂ fullName 需求基础实现
英美等地的⼈的名字通常分为 firstName 和 lastName ,⼆者的组合是全名 fullName 。类似于我们的姓和名。store.state 的数据定义如下:
state:{
firstName:"",
lastName:""
},
在 CpnThree 组件中 input 输⼊ firstName 和 lastName,并通过 v-model 指令将属性值与 state 状态变量进⾏绑定。并显示 fullName 全名,fullName 是 firstName 和 lastName 的组合。VuexCpnThree 组件代码如 下:
<template>
<div>
<label for="firstName">firstName:</label>
<input type="text" v-model="$store.state.firstName" id="firstName" />
<label for="lastName">lastName:</label>
<input type="text" v-model="$store.state.lastName" id="lastName" />
<div>VuexCpnThree组件</div>
<div>fullName:{{$store.state.firstName }}-{{$store.state.lastName }}</div>
</div>
</template>
在 VuexCpnFour 组件中也显示 fullName 全名。VuexCpnFour 组件代码如下:
<template>
<div>
<div>VuexCpnFour组件</div>
<div>fullName:{{$store.state.firstName }}-{{$store.state.lastName }}</div>
</div>
</template>
将上⾯的两个组件,引⽤到同⼀个⽗组件⾥⾯,最后的显示结果如下:
- 当我们在第⼀个组件⾥⾯输⼊ firstName 和 lastName 的时候,另⼀个组件 fullName 也随之发⽣变化。
- 使⽤ v-model 指令绑定了
$store.state.firstName
和$store.state.lastName
状态数据。 - 我们看到上⾯代码中的 fullName 是通过
{{$store.state.firstName }}-{{$store.state.lastName }}
拼接⽽成的,在两个组件⾥⾯分别进⾏计算得出 fullName。
三、v-model 绑定 state 状态数据的标准做法
前⾯我们使⽤ v-model 绑定了 $store.state
状态数据,实现了输⼊框与 state 状态数据之间的绑定,但是不推荐这种做法。因为这种直接的绑定⽅式,状态⽆法被 devtools 跟踪。
浏览器显示上已经为 zhang-san ,但是调试⼯具中的 firstName 和 lastName 仍然是空串。⽐较正规的做法是:v-model 绑定计算属性。
<label for="firstName">firstName:</label>
<input type="text" v-model="firstName" id="firstName" />
然后在 computed 计算属性的 get 和 set ⽅法⾥⾯进⾏ state 变量的状态管理。
computed: {
firstName:{
get(){
return this.$store.state.firstName
},
set(newVal){
this.$store.commit('handleFirstNameVal', newVal)
}
}
},
在 mutation 是⾥⾯定义 handleFirstNameVal,对 firstName 状态赋值。这种⽅式虽然较上⼀⼩节的实现麻烦了很多,但确实是标准的做法。这样做完之后 firstName 状态数据就可以正确的被 devtools s 所跟踪。
handleFirstNameVal(state,payload){
state.firstName = payload
}
四、vuex 的 getters 的定义与使⽤
在上⾯的实现中,我们看到全名 fullName 的值是通过 {{ $store.state.firstName }}-{{ $store.state.lastName }}
拼接⽽成的,在两个组件⾥⾯分别使⽤。还有⼀种⽅式就是将 firstName 和 lastName 先计算出 fullName,然后在组件⾥⾯使⽤ fullName,这种⽅法就是 getters。
state:{
firstName:"",
lastName:""
},
getters:{
fullName(state){
console.log(state.firstName +"-"+ state.lastName);
return state.firstName + "-" + state.lastName
}
},
下⾯代码显示效果和使⽤ {{ $store.state.firstName }}-{{ $store.state.lastName }}
是⼀样的。
<div>fullName:{{$store.getters.fullName}}</div>
说明:使⽤ getters 对 state 数据进⾏计算,不管有多少组件引⽤了 getters,getters 都只计算⼀次并且对计算结果进⾏缓存,后续的 getters 被组件调⽤都是⽤缓存结果。
只要 state 数据不发⽣变化,缓存结果就不发⽣变化。这与 computed 计算属性的表现是⼀致的,所以只要理解了 conputed 计算属性,getters 就是针对 state 的全局计算属性,这样就很容易使⽤和理解。
5.Vuex 状态异步操作
⼀、本节⼤纲
- Mutation 为什么不能做异步操作
- Action 基本⽤法
- 结合 Promise 使⽤ Action
⼆、Mutation 为什么不能做异步操作
我们尝试⼀下在 mutation 中定义⼀个异步操作,并做⼀个实验。代码如下
mutations:{
submitBtn(state){
//setTimeout异步操作
setTimeout(() => {
state.firstName = "zhang"
}, 1000);
}
},
然后在组件中 commit 触发这个异步操作
<label for="firstName">firstName:</label>
<input type="text" v-model="firstName" id="firstName" />
<button @click="$store.commit('submitBtn')">触发异步操作</button>
点击“触发异步操作”显示结果如下:界⾯上的 firstName 已经变成了 zhang ,但是 devtools 跟踪的结果 firstName 却仍然是⼀个空串。也就是说在 Mutation 的异步操作中,devtools ⽆法正确的跟踪 state 状态。
所以 Mutation 中只能定义同步操作,异步操作交给 Action。
三、Action 基本⽤法
- 在组件中使⽤ dispatch 触发异步操作 Action(如⽹络请求 backend API,后端服务 API )
- 在异步操作 Action 中对 Mutation 操作进⾏ commit
mutations:{
submitBtn(state){
//setTimeout(() => {//这⾥不能做异步操作
state.firstName = "zhang"
//}, 1000);
}
},
actions:{
submitAction(context){
setTimeout(() => {//异步操作
//state数据的修改还是由mutation执⾏
context.commit('submitBtn');
}, 1000);
}
},
- action 的⽅法的参数 context 意为上下⽂,在我们还没有学习 vuex 的 module 之前可以暂且认为它是
$store
对象。我们后⽂再做详解。 - 最后我们可以在组件中通过 dispatch ⽅法触发 Action。这样的操作结果就是:
state.firstName
在异步操作中也可以被 devtools 跟踪状态。
<label for="firstName">firstName:</label>
<input type="text" v-model="firstName" id="firstName" />
<button @click="$store.dispatch('submitAction')">触发异步操作</button>
四、结合 Promise 使⽤ Action
我们已经知道了 Vuex 的 Action ⽤来执⾏异步操作。假设有这样⼀个需求,希望在组件中发起异步操作 (如:⽹络请求),并在组件中针对⽹络请求的结果数据进⾏处理(⽽不是在 Action 或 mutation 中)。 这就要使⽤到 Promise。
- 在 Action 操作中⽤ Promise 包裹异步操作
- 将 Promise 作为 Action 的返回值
- 在组件中⽤返回的 Promise 对象进⾏ then ⽅法操作
actions:{
submitAction(context,payload){
return new Promise((resolve,reject) =>{
setTimeout(() => {
context.commit('submitBtn');
resolve("异步操作完成了" + payload)
}, 1000);
})
}
},
action ⽅法同样可以使⽤ payload 传参,传参的⽅式与 mutation ⼀致。
我们修改⼀下 action 的触发⽅式以及回调数据处理:
<button @click="demoAction()">触发异步操作</button>
methods: {
demoAction(){
this.$store.dispatch('submitAction',"demoAction")
.then(data =>{
console.log(data)
})
}
},
- 我们向 action 传递了⼀个参数:字符串”demoAction”。
- 在异步操作中使⽤ resolve 函数,说明 Promise 异步操作被正确执⾏。并将异步操作结果返回。
- ⽤ promise 的 then ⽅法接收异步请求的结果,此处针对结果只做简单的 log 打印处理:“异步操作完成了 demoAction。”
附加:对象的解构赋值
actions:{
submitAction(context){
setTimeout(() => {//异步操作
context.commit('submitBtn');
}, 1000);
}
},
换⼀种写法,仔细对⽐代码。
actions:{
submitAction({commit,state,getters}){
setTimeout(() => {//异步操作
commit('submitBtn');
}, 1000);
}
},
- ⾸先 context 是⼀个上下⽂对象,我们可以暂且认为它是
$store
对象。 - 将
$store
对象解构,使⽤三个对象 commit,state,getters 分别进⾏解构赋值。
6.modules 模块划分
⼀、本节⼤纲
- 引⼊ Modules 的意义
- 如何组织 Module 代码
- 模块外部引⽤模块内的状态
- 模块内引⽤全局的状态
⼆、引⼊ Modules 的意义
- Vuex 进⾏集中状态管理,可以认为它是单例模式。
- 当应⽤变的⾮常复杂的时候,所有的状态都定义在⼀个 store 对象⾥⾯会显得⼗分的臃肿。
- 为此,Vuex 提供了从语法的⻆度将 store 分割为多个模块的⽅法。每个模块可以拥有⾃⼰的 state、 mutations、actions、getters 。虽然区分了模块,但是状态仍然是集中管理的,也同样⽀持响应式状态管理。
三、如何组织 Module 模块代码
四、模块外部引⽤模块内的状态
对于如下的 A 模块定义的变量及状态该如何被外部引⽤调⽤?
const moduleA = {
state: { counter: 0 },
mutations: {
increment(state) {
// 这⾥的 `state` 对象是模块内的局部状态
state.counter++;
},
},
getters: {
doubleCount(state) {
return state.counter * 2;
},
},
};
- ⾸先我们要明确⼀点,这⾥的 state ( mutation 和 getters 参数)不再是全局的 state,⽽是模块内部的局部的模块的 state。
- 模块外部正确的引⽤模块内状态的⽅法是:
$store.state.a.counter
,⽽不是$store.state.counter
。 - 但是,模块外部调⽤ mutations 仍然是
$store.state.commit
,⽽不是$store.state.a.commit
。 getters、actions 同理。
五、模块内引⽤全局的状态
const moduleA = {
actions: {
incrementRootSum({ state, commit, rootState, rootGetters }) {
if ((state.count + rootState.count) % 2 === 1) {
commit("increment");
}
},
},
getters: {
sumWithRootCount(state, getters, rootState) {
return state.count + rootState.count;
},
},
};
- rootState 代表全局定义注册的状态变量,rootGetters 代表全局定义的 getters。注意前缀 root 在模块内代表全局的,不带 root 前缀的都是模块内局部定义的。可以使⽤⼆者在模块内部使⽤全局定义。
- 上⾯代码 state 代表模块内部定义的状态变量,getters 参数代表模块内部的局部定义的 getters。
- 还要注意⼀点,我们之前讲过 action ⽅法的参数 context,在没有模块之前我们可以认为 context 是全局对象 store ,在模块内 context 代表当前模块 Module 本身。上⾯代码中引⽤了 context 当前模块的{ state,commit,rootState, rootGetters }
六、store ⽂件⽬录组织
现在所有的状态及状态管理代码都是写到⼀个⽂件夹 store 的 index.js ⾥⾯,随着项⽬的扩⼤,这样不利于书写、查找及管理。通常我们需要将这些代码进⾏ js 的模块化管理。如:ES6 的模块化、CommonJS 模块化等。
store |
--index.js | //模块组装导出⼦⽂件
--actions.js | //根级别的actions
--mutations.js | //根级别的mutations
--getters.js | //根级别的getters
--modules |
--moduleA.js | //模块A状态管理⽂件
--moduleB.js; //模块B状态管理⽂件
将 actions、mutations、getters、modules 代码写到单独的⽂件并 export 导出,在 index.js 中 import 导⼊。最终形成的⽬录结构如上所示,请参考上⽅⽬录结构的。
6.1.全局的状态管理⽂件拆分
我们⾸先创建三个根级别的⽂件 actions.js,mutations.js ,getters.js ,这三个⽂件⽤于定义全局的状态管理代码。以 mutations.js 为例,代码如下(因为我们还没有做任何的定义,所以先导出空对象):
export default {
//在这⾥定义mutations
};
然后在 store/index.js 中将它们导⼊并使⽤⽣效。
6.2.分模块的状态管理
- ⾸先新建⼀个⽂件夹 store/modules ,⽤于存放分模块的状态管理信息。
- 假如我们需要管理登录⽤户的状态信息,我们新建⼀个 store/modules/sysuser.js ⽂件。内容如 下:可以在其内部定义与“⽤户状态”相关的状态管理代码。最后导出。