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