Vuex,用于管理分散在 Vue 各个组件中的数据。
每一个 Vuex 应用的核心都是一个 store(仓库),也可以理解它是一个"非凡的 全局对象"。
与普通的全局对象不同的是,基于 Vue 数据与视图绑定的特点,当 store 中 的状态发生变化时,与之绑定的视图也会被重新渲染。
这是一个单向的过程,因为 store 中的状态不允许被直接修改。改变 store 中的状态 的唯一途径就是显式地提交(commit)mutation,这可以让我们方便地跟踪每一个状态的 变化。(在大型复杂应用中,如果无法有效地跟踪到状态的变化,将会对理解和维护代 码带来极大的困扰。假如你能很好地理解使用 Vuex 进行状态管理的缘由,你就应该尽力 遵循"显式"的原则,即使你可以跳过这个过程。)
Vuex 中有 5 个重要的概念:State、Getter、Mutation、Action、Module。
1. State
state的用法如下:
CreateStore({
state:{
count:1
}
})
在组件中,可以直接使用$store.state.count(前提是store已经被注册到实例中),也可以先用mapState辅助函数将其映射下来。
代码如下:
import {mapState} from 'vuex'
export default {
computed:{
...mapState(['count']) // 是ES6中的对象展开运算符
}
}
2.Getter
Getter 维护由state派生的一些状态。这些状态随着State的变化而变化。
与计算属性一样,Getter 中的派生状态在被计算之后会被缓存起来,当重复调用时,如果被 依赖的状态没有变化,那么 Vuex 不会重新计算派生状态的值,而是直接采用缓存值。
Getter 的用法如下:
createStore({
state:{
count:1
},
getters:{
tenTimesCount(state){ // Vuex为其注入state对象
return state.count* 10;
}
})
在组件中。可以直接使用 $store.getters.tenTimesCount ,也可以先用mapGetters 辅助函数映射下来。代码如下
import {mapGetters} from 'Vuex'
export default {
computed :{
... mapGetters(['tenTimesCount']); // ... 是ES6中的对象展开运算符
}
}
3. Mutation
Mutation 提供修改State状态的方法
Mutation 的用法如下:
newVuex.Store({ // 创建仓库
state:{
count:0
},
mutations:{
addCount(state,num){
state.count+= num ||1
}
}
})
在组件中,可以直接使用store.commit 来提交mutation,代码如下:
methods:{
addCount(){
this.$store.commit('addCount') // store 被注入Vue实例中后可以使用this.$store
}
}
也可以先用MapMutation 辅助函数现将其映射下来
import {mapState.mapMutations} from 'vuex'
export default {
computed:{
...mapState(['count']) // ... 是es6中的对象展开运算符
},
methods:{
...mapMutations(['addCount']);
...mapMutations({
increaseCount:'addCount'
})
}
}
4. Action
Action类似Mutation两者的不同之处在于:
● Action 不能直接修改状态,只能通过提交 mutation 来修改。
● Action 可以包含异步操作。
Action的用法如下:
createStore({
state:{
count:0
},
mutations:{
addCount(state,num){
state.count+= num ||1
}
},
actions:{
// context具有和store实例相同的属性和方法
//可以通过context获取state和getter属性里的值、或者提交mutation和分发其他的action
addCountAsync(context,num){
setInterval( function(){
if(context.state.count<2000 ){
context.commit('addCount',num || 100)
}
},num || 100)
}
}
})
在组件中,可以直接使用store.dispatch来分发action。代码如下:
methods:{
addcountAsync(num){
this.$store.dispatch('addAsync',num)
}
}
或者使用mapActions辅助函数将其先映射下来。代码如下:
import {mapState,mapActions} from 'vuex'
export default{
computed:{
..mapState(['count']) ; // ... 是ES6中的展开运算符
},
methods:{
...mapActions(['addCountAsync']),
...mapActions([{
increaseCountAsync:'addCountAsync'
}])
}
}
5.Module
由于使用单一状态树,当项目的状态非常多时,store 对象就会变得十分臃肿。因 此,Vuex 允许我们将 store 分割成模块(Module),每个模块拥有独立的 State、Getter、 Mutation 和 Action,模块之中还可以嵌套模块,每一级都有着相同的结构。
Module 的用法如下:
// 定义模块
const counter = {
namespaced: true, // 定义为独立的命名空间
state: {
count: 0
},
getters: {
// 在模块中,getter方法还有rootState、rootGetters参数可以获取根模块中的数据
tenTimesCount(state, getters, rootState, rootGetters) {
return state.count * 10;
}
},
mutations: {
addCount(state, num) {
state.count += num || 1;
}
},
actions: {
// context具有和store实例相同的属性和方法
// 可以通过context获取state和getters里的值,或者提交mutation,分发action
// 在模块中,context还会具有rootState和rootGetters属性以获取根模块中的数据
addCountAsync(context, num) {
// 保存定时器ID以便后续可以清除
const timerId = setInterval(() => {
if (context.state.count < 2000) {
context.commit('addCount', num || 100);
} else {
clearInterval(timerId); // 达到条件后停止
}
}, num || 100);
// 返回定时器ID,可以在需要时清除
return timerId;
}
}
};
// 创建仓库(假设这是Vuex 3.x的语法,Vuex 4.x用法略有不同)
import { createStore } from 'vuex'; // Vuex 4.x
// 或者对于Vuex 3.x:
// import Vue from 'vue';
// import Vuex from 'vuex';
// Vue.use(Vuex);
const store = createStore({
modules: {
counter // 注册模块
}
});
export default store;
在组件中,模块的使用方式如下:
import {mapState,mapGetters,mapMutations.mapActions} from 'Vuex'
export default {
computed:{
//辅助函数的第一个参数为模块的名称
...mapState('counter',['count']);
...mapGetters('counter',['tenTimesCount'])
},
methods:{
...mapMutatiuons('counter',['addCount']),
...mapActions('counter',['addCountAsync'])
}
}
最后,结合 Vuex 用于管理分散在各个组件中的状态和追踪状态变更的初衷,简单总结了这些概念如下。作为一个状态管理器,首先要有保管状态的容器------State;为 了满足衍生数据和数据链的需求,从而有了 Getter;为了可以"显式地"修改状态,所 以需要 Mutation;为了可以"异步地"修改状态(满足 AJAX 等异步数据交互),所以 需要 Action;最后,如果应用有成百上千个状态,放在一起会显得十分庞杂,所以分模 块管理(Module)也是必不可少的。
Vuex 的用法如上,应该并不难理解。
Vuex 并不是 Vue 应用开发的必选项,在使用时,应先考虑项目的规模和特点,有选 择地进行取舍,盲目地选用只会带来更多的开发成本。
Vuex 为开发者提供了多种写法,不过并不推荐过多尝试和写法上的变换,毕竟 保持一致的风格也是高质量代码的一种表现,除非这种变化是一种进步。
本章所介绍的工具和插件均是由 Vue 官方提供并维护的,可以选择用或不用,取 决于实际情况