持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第19天,点击查看活动详情
前言
大家好,上篇文章vuex不常用的辅助函数mapMutations中我们分享了vuex中的另一个不常用辅助函数mapMutations,简单介绍了其使用方法,使用场景及实现原理,简单总结其实就是将methods自动映射为this.$store.commit('xxx')的形式。今天我们继续来分享vuex中另一个与mapMutations非常类似的不常用的辅助函数mapActions,它也是用来映射methods的,只不过与mapMutations不同的是它是将methods映射为this.store.dispatch('xxx'),下面我们来详细分析一下。
mapActions的用法及场景
在前面vuex使用介绍一文中我们知道,如果想要修改state中的状态值,必须要通过mutation中的方法直接修改,也可以通过action中的方法间接修改,不同的是mutation中的方法必须是同步的,而action中的方法则是异步的,那么这个actions跟mutations同理:如果在一个组件中我们要调用很多个action中的方法,那就要写很多次的this.$store.dispatch('xxx'),这时vuex又为我们提供了另外一个辅助函数mapActions
,其用法跟前面的mapMutations一摸一样也是将对应的action中的方法名以数组或对象的形式传递给mapActions,然后mapActions就会自动为我们将methods中的方法映射为sore.dispatch。vuex官方对于mapActions是这样说明的:
你可以在组件中使用
this.$store.dispacth('xxx')
分发actions,或者使用mapActions
辅助函数将组件中的 methods 映射为store.dispatch
进行分发(需要在根节点注入store
)。
methods: {
...mapActions([
'increment' // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
]),
...mapActions({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
})
}
复制代码
从上面代码我们可以看出mapActions的参数可以接收数组或者和对象的形式,如果传递的是一个数组,那么在methods中就会生成一些对应的以这个数组中的每个项命名的方法,然后就可以直接通过this.xxx来使用,而this.xxx就会被映射为this.$store.dispatch('xxx')。如果需要传递参数则跟使用普通方法一样直接传递即可,如:this.xxx('aaa'),同样会被映射为this.store.dispatch('xxx','aaa')。如果传递的是对象,则会以对象的属性名为名生成对应的methods,其效果和使用方式跟数组也是一样的。
源码解读
了解了mapMutations的使用方法是使用场景,下面我们再来分析下它的源码,看看它是如何做到将methods中的方法映射为sore.commit的。
// src/helpers.js 168行
function normalizeNamespace(fn){
return (namespace, map)=>{
if(typeof namespace !== 'string'){
map = namespace
namespace = ''
}else if(namespace.charAt(namespace.length - 1) !== '/'){
namespace += '/'
}
return fn(namespace, map)
}
}
export const mapActions = normalizeNamespace((namespace, actions)=>{
const res = {}
if (process.env.NODE_ENV !== 'production' && !isValidMap(actions)){
console.error('[vuex] mapActions: mapper parameter must be either an Array or an Object')
}
normalizeMap(actions).forEach(({key, val})=>{
res[key] = function mappedAction(...args){
let dispatch = this.$store.dispatch
if(namespace){
const moudle = 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的源码跟mapMutations的源码几乎是一模一样的,就是将commit换成了dispatch,同样normalizeNamespace和normalizeMap两个函数又出现了,这两个函数的源码在前面文章中已经分析过了,这里就不再详细解读了。下面我们重点来分析下传给normalizeNamespace的回调函数
- 首先组件中调用mapActions方法并传递namespace和actions两个参数进行执行(第一个参数可省略),然后在该函数内部:
- 先检测传进来的namespace参数是否是一个字符串类型,因为这个参数是可以不传的,如果不是字符串则说明只传递了一个参数,因此直接将namespace的值赋值给map,然后让namespace等于一个空字符串
- 最后再调用fn函数,并将执行结果返回
- 而在fn函数体内首先定义一个空对象res,用于保存对应的methods
- 检测第二个参数actions是否是一个对象或者数组,因为该方法只接收数组或对象类型的参数
- 这里normalizeMap函数的目的很简单就是把传进来的对象或数组统一转换成[{key,val}]的形式
- 然后遍历数组中的每个对象,并以对象中的key作为res对象的属性名,属性值则是一个新的函数mappedAction,而这个函数其实就相当于是我们定义在methods中对应的那个方法,当我们在组件中调用this.xxx方法时,实际上执行的就是这里面的代码。下面我们再来看下这里面又干了哪些事
- 首先拿到store实例上的dispatch方法
- 然后如果传递了namespace参数,则根据namespace值找到对应的模块,并将模块上下文中的dispatch重新赋值给变量commit
- 紧接着这一步才是关键,判断对象中的val是否是一个函数,如果是函数则直接让函数执行,并将dispatch和原参数args一同传递过去。翻译过来就是这样的:
this.val(dispatch, ...args)
- 如果不是函数,则直接让dispatch执行,并让dispatch中的this指向store实例,同时将val和原参数args一同传递,翻译过来:
this.$store.dispatch(val, ...args)
,实际上就是在这里帮我们调用了this.store.dispatch('xxx',...args)
以上就是mapActions的整体源码解析了。
总结
本次我们分享了vuex我们提供的一个不是很常用的辅助函数mapActions的使用方法及实现原理,简单总结如下:
首先将要提交的action对象中的方法名作为数组或对象传递给mapActions函数,在该函数中会定义一个新的函数mappedAction,并将其保存在res对象中,当我们在组件中调用methods中对应的方法时就会执行这个mappedAction方法,然后将真正action对象中的函数执行,从而实现将methods映射为this.$store.dispatch
关于vuex系列的源码解读就分享到这里了,下一篇文章开始我们将一起来学习一个vue-router系列的源码解读。 本次分享就到这里了,欢迎大佬们指点!!!