在我们使用vuex调取公共数据时,当一个组件需要获取多个状态或调用多个方法的情况下,将这些状态都声明为计算属性、多次书写引用方法,会有些重复和冗余。为了解决这个问题,我们使用辅助函数mapState、mapMutations、mapActions、mapGetters,主要目的是为了简洁自己的代码,对引用数据进行统一管理。
以项目的store中module为doctorInfo的模块来举例:
export default {
namespaced: true,
state: {
data: {},
},
mutations: {
setDoctorInfo(state, value) {
state.data = { ...state.data, ...value };
},
},
actions: {
asyncGetInfo({ commit, state }, value) { }
},
getters: {
getDataParams: state => key => {
return state.data[key];
},
}
};
目前项目中使用时,是根据以下方法调取数据:
export default{
computed: {
dataInfo() {
return this.$store.state.doctorInfo.data
},
newParam() {
return this.$store.getters['doctorInfo/getDataParams']
}
},
methods: {
setDoctorInfo() {
return this.$store.commit('doctorInfo/setDoctorInfo', info)
},
getInfo(params) {
return this.$store.dispatch('doctorInfo/asyncGetInfo', params);
}
}
}
辅助函数的映射关系:
mapState > computed
mapGetters > computed
mapMutations > methods
mapActions > methods
使用了辅助函数mapState、mapMutations、mapActions、mapGetters代码会显得简单很多:
import {mapState、mapMutations、mapActions、mapGetters} from 'vuex'
export default {
computed: {
...mapState('doctorInfo', {
dataInfo: state => state.data // 自定义命名dataInfo
}),
// 或者
// ...mapState({
// dataInfo: state => state.doctorInfo.data // 自定义命名dataInfo
// }),
...mapGetters('doctorInfo', ['getDataParams'],
},
methods: {
...mapMutations('doctorInfo', ['setDoctorInfo']),
...mapActions('doctorInfo', ['asyncGetInfo']),
}
}
上面可以看出mapState把doctorInfo模块state中的data映射成当前this下的dataInfo,但是mapActions, mapMutations, mapGetters这些怎么办,直接一个[]是很方便,但是如果引入方法和vue实例中的方法命名重复了,或者想对引入方法进行自定义命名,这种情况下该怎么办呢?
官网中只是这么讲的:
...mapActions([
'some/nested/module/foo', // -> this['some/nested/module/foo']()
'some/nested/module/bar' // -> this['some/nested/module/bar']()
])
或者:
...mapActions('some/nested/module', [
'foo', // -> this.foo()
'bar' // -> this.bar()
])
一般情况下,这种是够用的,但对于解决我们上面说的命名重复问题,很明显没有什么帮助。。那就只能去看看vuex中的源码,去看看对于mapActions这些方法是如何进行传参的:
var mapActions = normalizeNamespace(function (namespace, actions) {
var res = {};
if ( !isValidMap(actions)) {
console.error('[vuex] mapActions: mapper parameter must be either an Array or an Object');
}
normalizeMap(actions).forEach(function (ref) {
var key = ref.key;
var val = ref.val;
res[key] = function mappedAction () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
// get dispatch function from store
var dispatch = this.$store.dispatch;
if (namespace) {
var module = getModuleByNamespace(this.$store, 'mapActions', namespace);
if (!module) {
return
}
dispatch = module.context.dispatch;
}
return typeof val === 'function'
? val.apply(this, [dispatch].concat(args))
: dispatch.apply(this.$store, [val].concat(args))
};
});
return res
});
源码中mapActions(mapState、mapMutations、mapGetters也一样)一开始都会调用一个normalizeMap 的方法,我们来看下这个normalizeMap方法:
/**
* Normalize the map
* normalizeMap([1, 2, 3]) => [ { key: 1, val: 1 }, { key: 2, val: 2 }, { key: 3, val: 3 } ]
* normalizeMap({a: 1, b: 2, c: 3}) => [ { key: 'a', val: 1 }, { key: 'b', val: 2 }, { key: 'c', val: 3 } ]
* @param {Array|Object} map
* @return {Object}
*/
function normalizeMap (map) {
if (!isValidMap(map)) {
return []
}
return Array.isArray(map)
? map.map(function (key) { return ({ key: key, val: key }); })
: Object.keys(map).map(function (key) { return ({ key: key, val: map[key] }); })
}
可以看出,是它在处理mapActions中传入的第二个参数 ['asyncGetInfo'],normalizeMap会把入参转化成[{key: 'asyncGetInfo', val: 'asyncGetInfo'}]数组,我们可以看到如果传入的不是数组的话,normalizeMap会把入参当做对象来处理,依然把对象转化为{key: xxx, val: xxx,}的形式,最后再以数组返回。
所以要解决上面函数名重复的问题,我们可以在mapActions的第二个参数传入一个对象就行了,问题基本就快解决了,但这个对象的key是'asyncGetInfo'还是自定义命名(例如getInfo)呢?
这是我们就得回到mapActions的源代码了,看看最终执行的是key还是val了:
return typeof val === 'function'
? val.apply(this, [dispatch].concat(args))
: dispatch.apply(this.$store, [val].concat(args))
可以看出,真正执行actions的是根据val,所以我们的问题有答案了,直接自定义命名方法:(第二种写法)
methods: {
...mapActions('doctorInfo', {
getInfo: 'asyncGetInfo'
}),
}
根据源码的 typeof val === 'function', 以下一段可以看出, 还是有另外一种用法的:
return typeof val === 'function'
? val.apply(this, [dispatch].concat(args))
: dispatch.apply(this.$store, [val].concat(args))
第三种用法:
methods: {
...mapActions('doctorInfo', {
getInfo(dispatch, params){
dispatch('asyncGetInfo', params);
}
}),
}