Vue之Vuex

74 阅读4分钟

Vue的整体架构设计显得兼容性的异常强大,这很得益于它的第三者设计结构,进行了高度的低耦合,组件与组件之间的关联性通过第三者进行关联。

前文讲到的事件总线其实就是这种设计思维:

import Vue from 'vue'

const bus=new Vue()

export default bus

将上面的bus定义到Vue原型上即可进行全局事件总线的使用

其实所谓的状态管理同样是一个全局的第三者,可以共享于所有组件中,通过标识来触发该组件于各种其它组件之间的通信(状态获取以及状态改变)。

这种低耦合的设计在Vue中随处可见,那么Vuex作为状态管理的中心组件,作用是为了能够实现状态的共享,它主要涉及state(状态定义及存储)Getter(状态衍生及获取),Mutation(状态同步改变)Action(状态异步改变),Module(状态模块划分,大型项目基础)。总的来说就是五个内容,其中可以在Vuex中学到的基本函数及关键字涵盖一下内容:

mapState
mapGetters
mapMutations
mapActions

context
{commit}

this.$store.commit
this.$store.dispatch

下面我们将从五个方面进行学习

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    count: 0,
    user:{
        name:'NB',
        age:13
    }
  },
  mutations: {
    increment(state) {
      state.count++;
    },
    decrement(state) {
      state.count--;
    }
  },
  actions: {
    increment(context) {
      context.commit('increment');
    },
    decrement(context) {
      context.commit('decrement');
    }
  },
  getters: {
    getCount: state => {
      return state.count;
    }
  }
});

export default store;

State

State用于定义及存储数据,这些数据是响应式的

对于定义上文已经给出,那么获取数据一般有以下两种方式

//使用mapState进行获取
import { mapState } from 'vuex';

export default {
  computed: {
    // 在组件中可以通过 this.count 访问状态
    ...mapState(['count','user']) 
    //使用别名,组件中通过this.countAlia访问
    ...mapState({countAlia:'count',userAlia:'user'})
  }
}
//直接通过$store.state进行访问
export default {
  computed: {
    count() {
      // 访问状态树中的 count 属性
      return this.$store.state.count; 
    }
  }
}

Getters

用于State里面数据的衍生,当你需要在原数据的基础上进行计算生成其它数据时,便可以使用Getters。我们说过,Vuex的设计是一种第三者的思维设计,所以可以看成是一个全局组件,这个getters里面的属性,便是正常组件的computed属性。只要对应的state里面的属性发生改变,那么getters相关联的数据都会进行重新计算。

//定义
getters : {
  // 通过状态计算派生值
  doubleCount: state => {
    return state.count * 2; 
  }
};

//第一种获取方式
computed:{
    doubleCount:this.$store.getters.doubleCount
}

第二种获取方式
computed:{
    ...mapGetters(['doubleCount'])
    ...mapGetters({doubleAlia:'doubleCount'})
}

Mutations

state里面的状态属性怎么来改变呢?Mutation的作用就在这里。

Mutation作为一个可以修改state里面属性的存在,一般接受两个参数,(state,payload),第一个参数是state,前文所说的state,payload是额外参数。

 //定义
 mutations: {
    increment(state, payload) {
      state.count += payload.amount;
    },
    decrement(state, payload) {
      state.count -= payload.amount;
    }
  }
  
//在组件中触发方式1
 methods: {
    increment(amount) {
      this.$store.commit('increment', {amount:amount});
    },
    decrement(amount) {
      this.$store.commit('decrement', {amount:amount});
    }
  }

//在组件中触发方式二
computed:{
    ...mapMutations(['increment'])
    ...mapMutations({incrementAlia:'increment'})//别名
}
//mapMutations本身不支持参数的映射,但是我们可以直接传递
methods:{
    add(amount){
        this.increment({amount:amount})
    }
}

可以发现,使用的是commit来进行触发的,所以不得不提Mutation里面的函数只能写同步操作,不能进行异步(比如发送ajax请求)。

Actions

Actions的存在其实就是异步的Mutations。通过触发Mutation从而去改变state,那么它是怎么操作的呢?

同样,Actions里面的函数接收两个参数,(context,payload),context作为一个上下文,包含了整个Vuex的内容属性以及操作。payload作为一个参数,用来传递值的。

context内容属性

//定义
  state: {
    count: 0
  },
  mutations: {
    increment(state,payload) {
      state.count+=payload.amount;
    }
  },
  actions: {
    asyncIncrement(context,payload) {
      setTimeout(() => {
        context.commit('increment',payload);
      }, 1000);
    }
  }
  
//触发方式1
methods: {
    incrementAsync(amount) {
      this.$store.dispatch('asyncIncrement',{amount:amount});
    }
  }

//触发方式2
computed:{
    ...mapActions['asyncIncrement']
    ...mapActions({asyncIncrementAlia:'asyncIncrement'})//别名
}

整个流程非常简便,即是通过dispatch来触发Actions里面的函数,然后Actions里面函数通过commit来触发Mutations里面的函数,然后由Mutations里面的函数对state里面的属性进行更改,发现这和mvc分层架构又是相同的地方,哪哪都在进行低耦合。

当然,这种低耦合在个人看来,对于大型项目比较有吸引力,这也是为什么vue还能够一直存在的理由。

Module

顾名思义,这是模块的意思,在一个大型项目中,一般会进行模块划分,即分布式,不同模块对应不同的module,不同module进行了隔离,这也是开发中最常见的数据隔离方式。

需要注意的是,每一个module都是一个完整的状态管理器(组件),都有各自的state,getters,mutations,actions。所以有了命名空间的存在,即namespace,进行不同模块之间数据的隔离,以防发生冲突。

//moduleA.js
export default {
  namespaced: true,
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++;
    }
  },
  actions: {
    asyncIncrement({ commit }) {
      setTimeout(() => {
        commit('increment');
      }, 1000);
    }
  },
  getters: {
    doubleCount(state) {
      return state.count * 2;
    }
  }
};

//moduleB.js
export default {
  namespaced: true,
  state: {
    message: 'Hello'
  },
  mutations: {
    setMessage(state, payload) {
      state.message = payload;
    }
  },
  actions: {
    asyncSetMessage({ commit }, payload) {
      setTimeout(() => {
        commit('setMessage', payload);
      }, 1000);
    }
  },
  getters: {
    upperCaseMessage(state) {
      return state.message.toUpperCase();
    }
  }
};
//index.js

import Vue from 'vue'
import Vuex from 'vuex'
import moduleA from 'moduleA'
import moduleB from 'moduleB'

Vue.use(Vuex)

const store=new Vuex.Store({
  modules: {
      moduleA,
      moduleB
  }
})

export default store

上面是最基本的结构化module,根据项目需求的不同,可以进行不同的设计即可。

既然进行了结构化,那么我们上面讲到的map就需要稍微改变一下了,只要改变map的第一个参数即可,前文我们一直只使用了一个参数,现在变成了(moduleName,和不是module的第一个参数一样)

//map形式
computed: {
    ...mapState('moduleA', {
      countAlia: 'count'
    }),
    ...mapState('moduleA',['count'])
    
    ...mapGetters('moduleA', {
      doubleCountAlia: 'doubleCount'
    }),
    ...mapGetters('moduleA',['doubleCount'])
    
    ...mapMutations('moduleA', {
      incrementAlia: 'increment'
    }),
    ...mapMutations('moduleA',['increment'])
    
    ...mapActions('moduleA', {
      asyncIncrementAlia: 'asyncIncrement'
    }),
    ...mapMutations('moduleA',['asyncIncrement'])
    
    ...mapState('moduleB', {
      message: 'message'
    }),
    ...mapGetters('moduleB', {
      upperCaseMessage: 'upperCaseMessage'
    })
    //mutations和actions和moduleA的类似 .......
  },

上文是map形式的映射,如果不想使用map呢,我们同样可以使用原生的方式,差异不大,只需要加上模块名前缀即可。

export default {
  computed: {
    count() {
      return this.$store.state.moduleA.count;
    },
    doubleCount() {
      return this.$store.getters['moduleA/doubleCount'];
    },
    message() {
      return this.$store.state.moduleB.message;
    },
    upperCaseMessage() {
      return this.$store.getters['moduleB/upperCaseMessage'];
    }
  },
  methods: {
    increment(amount) {
      this.$store.commit('moduleA/increment',{amount:amount});
    },
    asyncIncrement(amount) {
      this.$store.dispatch('moduleA/asyncIncrement',{amount:amount});
    },
    setMessage(message) {
      this.$store.commit('moduleB/setMessage', {message:message});
    },
    asyncSetMessage(message) {
      this.$store.dispatch('moduleB/asyncSetMessage', {message:message});
    }
  }
};

以上就是Vuex的基本内容,可以从整体上看,它就是一个全局的组件,不过回调处理函数写在这个全局组件的内部,state里面的数据和一般组件的data里面的数据一样,是响应式的,getters和一般组件的computed属性一样,mutations+actions实现和一般组件的method方法一样。唯一不同的在于,它是全局的,是共享式的存在。