【源码解析】vuex中可能一辈子也用不到的辅助函数 - mapActions

持续创作,加速成长!这是我参与「掘金日新计划 · 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系列的源码解读。 本次分享就到这里了,欢迎大佬们指点!!!

猜你喜欢

转载自juejin.im/post/7108281186056667144