理解Vuex
什么是Vuex
概念:专门在Vue中实现集中式状态(数据)管理的一个Vue插件,对Vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。
什么时候用Vuex
- 多个组件依赖于同一个状态
- 来自不同组件的行为需要变更同一状态
(比如日常项目中一个全局使用的弹窗,需要在很多组件控制其显示和隐藏)
Vuex的工作原理
Vuex是集中于MVC模式中的Model层,规定所有的数据操作必须通过action-mutation-state的流程来进行,再结合Vue的数据视图v-model等双向绑定特性来实现页面的展示更新。如图所示:
简述主要方法详情:
- Vue Components:Vue组件。展示页面,负责接收用户操作等交互行为,执行dispatch方法触发对应action进行回应。
- dispatch:操作行为触发方法。是唯一能执行action的方法。
- actions:操作行为处理模块。处理Vue Components 接收到的所有交互行为。包含同步/异步操作,支持多个同名方法,按照注册顺序依次触发。像后台API请求的操作就在这个模块进行,包括触发其他action以及提交mutation的操作。该模块提供了Promise的封装,以支持action的链式触发。
- commit:状态改变提交操作方法。对
mutation进行提交,是唯一能执行mutation的方法。 - mutations:状态改变操作方法。是Vuex修改state的唯一推荐方法,其他修改方式在严格模式下将会报错。该方法
只能进行同步操作,且方法名只能全局唯一。操作之中会有一些hook暴露出来,以进行state的监控等。 - state:页面状态管理容器对象。集中存储Vue Components中data对象的零散数据,全局唯一,以进行统一的状态管理。页面显示所需的数据从该对象中进行读取,利用Vue的细粒度数据响应机制来进行高效的状态u更新。
总结一句话:
Vue组件接收交互行为,调用dispatch方法触发action相关处理,若页面状态需要改变,则调用commit方法提交mutation修改state,通过getters获取到state新值,响应数据或状态给Vue 组件,界面随之更新。
以案例走一遍Vuex完整流程
App.vue
<template>
<div>
<h1> 当前求和为:{{ $store.state.sum }}</h1>
<select v-model="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 {
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)
}
}
}
</script>
<style scoped></style>
store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
// 创建一个新的 store 实例
export const store = new Vuex.Store({
// state:用于存储数据
state() {
return {
sum: 0
}
},
// 准备actions--用于响应组件中的动作,此处适合处理一些业务逻辑
// jia和jian由于没有什么业务逻辑无需走actions这一层,可以直接用mutations
actions: {
// jia(context, value) {
// context.commit('JIA', value)
// },
// jian(context, value) {
// context.commit('JIAN', value)
// },
jiaOdd(context, value) {
if (context.state.sum % 2) {
context.commit('JIA', value)
}
},
jiaWait(context, value) {
setTimeout(() => {
context.commit('JIA', value)
}, 500)
}
},
// mutations:用于操作数据(state)
mutations: {
JIA(state, value) {
this.state.sum += value
},
JIAN(state, value) {
this.state.sum -= value
}
},
})
总结:对数据state的处理,完整流程是走actions与mutations这两步,actions主要处理一些业务逻辑,而mutations是用来直接改变state的一步。如果只是简单的改变一下state,则可以越过actions直接使用mutations一步到位。
getters配置项
Vuex允许我们在store中定义"getter"(可以认为store的计算属性)。就像计算属性一样,getter的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
getters的作用
对于getters的理解主要作用是对state属性进行计算,可以理解类似于Vue中computed。
基本使用:
store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
// 创建一个新的 store 实例
export const store = new Vuex.Store({
state() {
return {
count: 0
}
},
mutations: {
increment(state) {
state.count++
}
},
getters: {
getNum(state) {
return state.num
},
getCount(state) {
return state.count
}
}
})
App.vue
<template>
<div>
{{ count }}
<button @click="add">Add State</button>
</div>
</template>
<script>
export default {
data() {
return {}
},
computed: {
count() {
return this.$store.getters.getCount
}
},
methods: {
add() {
this.$store.commit('increment')
}
}
}
</script>
<style scoped></style>
mapGetters辅助函数写法
<template>
<div>
{{ getCount }}
<button @click="add">Add State</button>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
export default {
data() {
return {}
},
computed: {
count() {
return this.$store.getters.getCount
},
...mapGetters(['getCount'])
},
methods: {
add() {
this.$store.commit('increment')
}
}
}
</script>
<style scoped></style>
Vuex模块化+命名空间
1. 定义模块(Module)
在Vuex中,每个模块是一个包含以下属性的对象:
state: 模块内部的状态对象,存储模块特有的数据。
const myModule = {
state: {
count: 0,
// 其他状态属性...
},
};
2. 模块的getters
类似于全局状态的getters,模块内部也可以定义getters,它们是基于模块状态的计算属性:
const myModule = {
// ...
getters: {
doubledCount: (state) => state.count * 2,
// 其他getter...
},
};
3. 模块的mutations
模块的mutations负责同步更新模块状态,遵循全局mutations同样的规则:
const myModule = {
// ...
mutations: {
increment(state) {
state.count++;
},
decrement(state) {
state.count--;
},
// 其他mutations...
},
};
4. 模块的actions
模块的actions用于处理异步逻辑或者包含复杂业务流程的操作,它们通过调用commit来触发模块内的mutations:
const myModule = {
// ...
actions: {
asyncIncrement({ commit }) {
setTimeout(() => {
commit('increment');
}, 1000);
},
asyncDecrement({ commit }) {
// 异步操作后提交mutation
},
// 其他actions...
},
};
5. 在组件中使用模块化Vuex
访问模块的state和getters
- 直接访问:使用
this.$store.state.myModule.count或this.$store.getters['myModule/doubledCount']。 - 组件计算属性或辅助函数:使用
mapState和mapGetters辅助函数将模块状态映射到组件的计算属性。
import { mapState, mapGetters } from 'vuex';
export default {
computed: {
...mapState('myModule', ['count']),
...mapGetters('myModule', ['doubledCount']),
},
};
触发模块的mutations和actions
-
使用
commit调用模块内mutations:this.$store.commit('myModule/increment'); -
使用
dispatch调用模块内actions:this.$store.dispatch('myModule/asyncIncrement');
或者,同样可以使用mapMutations和mapActions辅助函数将模块的mutations和actions映射为组件方法:
import { mapMutations, mapActions } from 'vuex';
export default {
methods: {
...mapMutations('myModule', ['increment']),
...mapActions('myModule', ['asyncIncrement']),
},
};
6. 跨模块访问
-
若要从一个模块的
action或getter访问另一个模块的state、getters、actions或mutations,可以通过context参数:- 在
action中:someAction({ dispatch, commit, rootState, rootGetters, getters, state }) { // 使用rootState、rootGetters访问全局状态和getter // 使用getters、state访问当前模块的状态和getter // 使用dispatch('otherModule/action')或commit('otherModule/mutation')调用其他模块的方法 }
- 在
-
若在组件中跨模块访问,直接通过
$store的相应方法指定模块路径即可。