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作为一个参数,用来传递值的。
//定义
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方法一样。唯一不同的在于,它是全局的,是共享式的存在。