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

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第18天,点击查看活动详情

前言

大家好,上篇文章vuex不常用的辅助函数mapState中我们分享了vuex中的一个不常用的辅助函数mapState,简单介绍了其使用方法,使用场景及实现原理,说白了其实就是帮我们自动定义计算属性。今天我们继续来分享vuex中另一个不常用的辅助函数mapMutations,该函数与mapState极其类似,不同是它是来帮我们自动定义methods的,下面我们来详细分析一下。

mapMutations的用法及场景

在前面vuex使用介绍一文中我们知道,如果想要修改state中的状态值,不能直接修改必须要通过mutation中的方法进行修改,而mutation中的方法是通过this.$store.commit('xxx')方法进行调用的,跟state同理:如果在一个组件中我们要调用很多个mutation中的方法,那就要写很多次的this.store.commit('xxx'),这时vuex又为我们提供了另外一个辅助函数mapMutations,其用法跟前面的mapState一样也是将对应的mutations中的方法名以数组或对象的形式传递给mapMutations,然后mapMutations就会自动为我们将methods中的方法映射为sore.commit。vuex官方对于mapMutations是这样说明的:

你可以在组件中使用 this.$store.commit('xxx') 提交 mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)。

methods: {
    ...mapMutations([
      'increment' // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
    ]),
    ...mapMutations({
      add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
    })
  }
复制代码

从上面代码我们可以看出mapMutations的参数可以接收数组或者和对象的形式,如果传递的是一个数组,那么在methods中就会生成一些对应的以这个数组中的每个项命名的方法,然后就可以直接通过this.xxx来使用,而this.xxx就会被映射为this.$store.commit('xxx')。如果需要传递参数则跟使用普通方法一样直接传递即可,如:this.xxx('aaa'),同样会被映射为this.store.commit('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 mapMutations = normalizeNamespace((namespace, mutations)=>{
   const res = {}
   if (process.env.NODE_ENV !== 'production' && !isValidMap(mutations)){
       console.error('[vuex] mapMutations: mapper parameter must be either an Array or an Object')
   }
   normalizeMap(mutations).forEach(({key, val})=>{
       res[key] = function mappedMutation(...args){
           let commit = this.$store.commit
           if(namespace){
               const moudle = getModuleByNamespace(this.$store, 'mapMutations', namespace)
               if(!module){
                   return
               }
           }
           commit = module.context.commit
       }
       return typeof val === 'function' ? val.apply(this, [commit].concat(args)) : commit.apply(this.$store, [val].concat(args))
   })
   return res
})
复制代码

当看到这段代码时是不是很熟悉,没错这段代码跟我们上一篇文章分享的mapState源码大部分都是一样的,这里我们又见到了normalizeNamespace和normalizeMap两个函数,关于这两个函数的源码我们在上一篇已经分析过了,这里就不再详细解读了。实际上normalizeNamespace是一个柯理化函数,当在组件中调用mapMutations时,最终执行的是传给normalizeNamespace的那个回调函数,该函数也是实现将methods映射为this.$store.commit的核心所在,下面我们重点来分析下这个回调函数

  • 首先组件中调用mapMutations方法并传递namespace和mutations两个参数进行执行(第一个参数可省略),然后在该函数内部:
    • 先检测传进来的namespace参数是否是一个字符串类型,因为这个参数是可以不传的,如果不是字符串则说明只传递了一个参数,因此直接将namespace的值赋值给map,然后让namespace等于一个空字符串
    • 最后再调用fn函数,并将执行结果返回
  • 而在fn函数体内首先定义一个空对象res,用于保存对应的methods
  • 检测第二个参数mutations是否是一个对象或者数组,因为该方法只接收数组或对象类型的参数
  • 这里又多出来个normalizeMap函数,该函数的目的很简单就是把传进来的对象或数组统一转换成[{key,val}]的形式
  • 然后遍历数组中的每个对象,并以对象中的key作为res对象的属性名,属性值则是一个新的函数mappedMutation,而这个函数其实就相当于是我们定义在methods中对应的那个方法,当我们在组件中调用this.xxx方法时,实际上执行的就是这里面的代码。下面我们再来看下这里面又干了哪些事
    • 首先拿到store实例上的commit方法
    • 然后如果传递了namespace参数,则根据namespace值找到对应的模块,并将模块上下文中的commit重新赋值给变量commit
    • 紧接着这一步才是关键,判断对象中的val是否是一个函数,如果是函数则直接让函数执行,并将commit和原参数args一同传递过去。翻译过来就是这样的:this.val(commit, ...args)
    • 如果不是函数,则直接让commit执行,并让commit中的this指向store实例,同时将val和原参数args一同传递,翻译过来:this.$store.commit(val, ...args),实际上就是在这里帮我们调用了this.store.commit('xxx',...args)

以上就是mapMutations的整体源码解析了。

总结

本次我们分享了vuex我们提供的一个不是很常用的辅助函数mapMutations的使用方法及实现原理,简单总结如下:

首先将要提交的mutation对象中的方法名作为数组或对象传递给mapMutations函数,在该函数中会定义一个新的函数mappedMutation,并将其保存在res对象中,当我们在组件中调用methods中对应的方法时就会执行这个mappedMutation方法,然后将真正mutation对象中的函数执行,从而实现将methods映射为this.$store.commit

好了本次分享就到这里了,欢迎大佬们指点!!!

猜你喜欢

转载自juejin.im/post/7107943759765045256