vuex中的辅助函数在vue2.0和vue3.0的分别使用

前言

​ 最近自己一直在学习vue3,学习到了很多的新知识,非常的感谢coderwhy老师的指导。由于自己一直有做笔记这个习惯,学习到了vuex中的辅助函数,分别在vue2和vue3中的分别使用,以前感觉自己有必要记录下来,来对自己加深印象,巩固基础。

正题

1 、为什么要使用辅助函数

const store = new Vuex.Store({
    state: {
        name: 'james',
        age: 35,
        height: 2.03
        team: 'lakers'
    }
})

在上面创建store过程中,我们在state中,保存四个的数据,那么,我们在组件中的使用呢?

方式一:

<template>
    <div>	
        <h1>姓名:{{$store.state.name}}h1>
        <h1>年龄:{{$store.state.age}}h1>
        <h1>身高:{{$store.state.height}}h1>
        <h1>团队:{{$store.state.team}}h1>
    div>    
template>

​ 是不是感觉方式一,在{{}}写了太多太长的表达式了,而且这里还只有四个数据,如果数据多了,就感觉很麻烦了吧

方式二:

//采用计算属性
export default {
 	computed: {
        name() {
            return this.$store.state.name
        },
        age() {
            return this.$store.state.age
        },
        height() {
            return this.$store.state.height
        },
        team() {
            return this.$store.state.team
        }
    }   
} 

然后在模板中的使用

<template>
    <div>	
        <h1>姓名:{{name}}</h1>
        <h1>年龄:{{age}}</h1>
        <h1>身高:{{height}}</h1>
        <h1>团队:{{team}}</h1>
    </div>    
</template>

在模板中确实简便了不少,但是在计算属性中,还是需要写着很多的表达式,如果store中的数据多了,并且还在组件内部的计算属性,那么computed计算属性就非常的多(这也许就是vue2中options API的不足)。

​ vuex中的actions, mutations,getters都存在一样的现象, 所以针对上面的现象,vuex中推出了辅助函数,来方便我们解决此类的问题。

2、辅助函数的本质

vuex中提供了四个辅助函数mapState, mapMutations, mapGetters, mapActions

他们内部的源码都是通过 this.$store 来拿到store中的值的。

自己试着读一下,mapState的源码

//normalizeNamespace 是一个初始化命名空间的函数,也是返回一个函数
var mapState = normalizeNamespace(function (namespace, states) {
    var res = {};
    //这里判断是否是数组还是对象
    if (!isValidMap(states)) {
      console.error('[vuex] mapState: mapper parameter must be either an Array or an Object');
    }
    //normalizeMap函数就是用来处理数组或则对象(这个函数内部很简单)
    //最后返回一个集合对象 [{key: key, val: val}]
    normalizeMap(states).forEach(function (ref) {
        //获取到key和value的值
      var key = ref.key;
      var val = ref.val;
		
       //根据val,通过this.$store来到store的value值,然后保存在res对象中
      res[key] = function mappedState () {
        var state = this.$store.state;
        var getters = this.$store.getters;
        if (namespace) {
          var module = getModuleByNamespace(this.$store, 'mapState', namespace);
          if (!module) {
            return
          }
          state = module.context.state;
          getters = module.context.getters;
        }
        return typeof val === 'function'
          ? val.call(this, state, getters)
          : state[val]
      };
      // mark vuex getter for devtools
      res[key].vuex = true;
    });
    //最后包对象返回
    return res
  });

其他的辅助函数,是返回同样的数据结构,所以是一样处理的。

阅读上面的源码,我们很清楚的知道,辅助函数就返回的一个对象,对象的形式就是 key是字符串,value就是函数,大致的形式如下:

computed: {
    //这里也很容易清楚,这里为什么需要结构(因为返回的是一个对象,哈哈哈,啰嗦了)
    ...mapState(['name', 'age'])
}

//转化的形式如下
{
    name: function() {},
    age: function() {}
}

上面说了这么多,其实总结出来就两句话:

  1. 辅助函数返回一个对象,对象的形式是 key是字符串, value是函数

  2. 内部是实现使用this.$store来得到store中的值 (这里主要是在vue3中的setup时,要注意,setup中没有this)

3、辅助函数在vue2中的使用

辅助函数,就两种形式的写法,对象形式和数组形式

3.1 mapState

mapState一般在计算属性中使用

...mapState(['name', 'age'])    //数组形式
...mapState({                   //对象的形式
    name: state => state.name,
    age: state => state.age
})

3.2 mapGetters

mapGetters也是一般在计算属性中使用

...mapState(['translateName', 'translateAge'])    //数组形式
...mapState({                                     //对象的形式
    translateName: 'translateName',
    translateAge: 'translateAge'
})

3.3 mapMutations 和 mapActions

他们的使用方法和mapGettets的方式一样的,只不过在他们是在methods中使用

...mapMutations(['changeName', 'changeAge'])       //数组形式
...mapMutations({                                  //对象的形式
    changeName: 'changeName',
    changeAge: 'changeAge'
})

...mapActions(['asyncChangeName', 'asyncChangeAge'])       //数组形式
...mapActions({                                  //对象的形式
    changeName: 'asyncChangeName',
    changeAge: 'asyncChangeAge'
})

4、辅助函数在vue3中的使用

vue3中主要是使用setup函数来编写逻辑的,但是在setup中又没有this,所以我们不能通过this.$store来获取store,那么在vuex4中, 提供了一个钩子函数useStore

import { useStore } from 'vuex'
const store = useStore()

所以在setup中使用store,那么就变得简单了

import { useStore } from 'vuex'
import { computed } from 'vue'

setup() {
    const store = useStore()
    
    //还是使用计算属性包裹起来
    const name = computed(() => store.state.name)
    const age = computed(() => store.state.age)
    
    return {
        name,
        age
    }
}

是不是,写的很难受啊。是不是又回到最开始的疑问啦? 当数据变多的时候, 又会大大的增加代码量。所以,我们还是需要借助辅助函数。

4.1 mapState

在vue3的官网中,没有明确的提示到使用辅助函数,但是我们可以通过自己的分析,来使用辅助函数

我们知道计算属性computed是一个函数,接受一个函数作为参数, 我们还知道 辅助函数最后 被解析成一个对象,对象中的属性值也是函数,那我们怎么考虑结合起来呢?

是不是只要把对象中的属性值作为computed的参数就可以了。

使用辅助函数,封装一个useState的hooks函数

import { useStore, mapState } from 'vuex'
import { computed } from 'vue'

//mapper: Array | Object
const useState = function(mapper) {
    const store = useStore()
    
    //使用复制函数解析成一个对象
    const storeStateObj = mapState(mapper)
    const res = {}
    
    //通过Object.keys拿到对象的所有key值,遍历,取出对应的value值,也就是函数
    Object.keys(storeStateObj).forEach(item => {
        //这我们知道辅助函数的内部是通过this.$store来实现的
        //setup中没有this, 所以通过bind来改变this的指向
        const fn = storeStateObj[item].bind({$store, store})
        //拿到函数,作为计算属性的参数,最后在留在一个对象中
        res[item] = computed(fn)
    })
    
    //res是一个对象, key是字符串, value值是ref对象
    return res
}

export default useState

在组件中使用

import useState from './hooks/useState'

export default {
    setup() {
        const storeObj = useState(['name', 'age'])
        //当然对象也行
        return {
            ...storeObj
        }
}

是不是又可以愉快的写代码啦!

4.2 mapGetters

mapState 和 mapGetters的使用方式, 基本一样,也都是在computed计算属性中使用。

使用辅助函数, 封装一个useGetters的hooks函数

import { useStore, mapGetters } from 'vuex'
import { computed } from 'vue'

//mapper: Array | Object
const useGetters = function(mapper) {
    const store = useStore()
    
    //使用复制函数解析成一个对象
    const storeStateObj = mapGetters(mapper)
    const res = {}
    
    //通过Object.keys拿到对象的所有key值,遍历,取出对应的value值,也就是函数
    Object.keys(storeStateObj).forEach(item => {
        //这我们知道辅助函数的内部是通过this.$store来实现的
        //setup中没有this, 所以通过bind来改变this的指向
        const fn = storeStateObj[item].bind({$store, store})
        //拿到函数,作为计算属性的参数,最后在留在一个对象中
        res[item] = computed(fn)
    })
    
    //res是一个对象, key是字符串, value值是ref对象
    return res
}

export default useGetters

跟上面的useState基本是一样的。

4.3 useState和useGetters的高度封装

//common.js

import { useStore } from 'vuex'
import { computed } from 'vue'

const commonFn = function(mapper, mapFn) {
    const store = useStore()
    const storeStateObj = mapFn(mapper)
    const res = {}
    Object.keys(storeStateObj).forEach(item => {
        const fn = storeStateObj[item].bind({$store, store})
        res[item] = computed(fn)
    })
    return res
}

export default commonFn
//useState.js
import { mapState } from 'vuex'
import commonFn from './common.js'

const useState = (mapper) => {
    return commonFn(mapper, mapState)
}
export default useState
//useGetters.js
import { mapGetters } from 'vuex'
import commonFn from './common.js'

const useGetters = (mapper) => {
    return commonFn(mapper, mapGetters)
}
export default useGetters

4.4 mapMutations 和 mapActions

mutaiton本来就是一个方法,所以直接跟辅助函数的属性值挂钩,

import { mapMutations, mapActions } from 'vuex'

export default {
    setup() {
        //mutations
        const mutationsObj = mapMutations(['changeName', 'changeAge'])
        
        //等价于
        const mutationsObj = mapMutations({
            changeName: 'changeName',
            changeAge: 'changeAge'
        })
        
        //action
        const actionsObj = mapMutations(['asyncChangeName', 'anyncChangeAge'])
        
        //等价于
        const actionsObj = mapMutations({
            changeName: 'asyncChangeName',
            changeAge: 'anyncChangeAge'
        })
        
        return {
            ...mutationsObj
        }
    }
}

5、补充

在上面,我们辅助函数封装了useState, useGetters两个函数,来处理store中的值,但是呢?现在补充考虑不周到的是: 没有考虑到模块的情况,所以重新封装了一下。

//common.js

import { useStore } from 'vuex'
import { computed } from 'vue'

const commonFn = function(mapper, mapFn) {
    const store = useStore()
    const storeStateObj = mapFn(mapper)
    const res = {}
    Object.keys(storeStateObj).forEach(item => {
        const fn = storeStateObj[item].bind({$store, store})
        res[item] = computed(fn)
    })
    return res
}

export default commonFn

这里的common.js没有改变

借助vuex提供的createNamespacedHelpers函数来得到模块中的属性

//useState.js
import { mapState, createNamespacedHelpers } from 'vuex'
import commonFn from './common.js'

//第一个参数为模块的名称,为了在模块中,也可以使用辅助函数
const useState = (moduleName, mapper) => {
    let mapperFn = mapState
    if(typeof moduleName === 'string' && moduleName.length > 0) {
        //防止出现useState('', [])发生了
        mapperFn = createNamespacedHelpers(moduleName).mapState
    } else {
    	mapper = moduleName
    }
    return commonFn(mapper, mapperFn)
}
export default useState
//useGetters .js
import { mapGetters, createNamespacedHelpers } from 'vuex'
import commonFn from './common.js'

//第一个参数为模块的名称,为了在模块中,也可以使用辅助函数
const useGetters = (moduleName, mapper) => {
    let mapperFn = mapGetters
    if(typeof moduleName === 'string' && moduleName.length > 0) {
        //防止出现useState('', [])发生了
        mapperFn = createNamespacedHelpers(moduleName).mapGetters
    } else {
    	mapper = moduleName
    }
    return commonFn(mapper, mapperFn)
}
export default useGetters 

createNamespacedHelpers的源码

 var createNamespacedHelpers = function (namespace) { return ({
    mapState: mapState.bind(null, namespace),
    mapGetters: mapGetters.bind(null, namespace),
    mapMutations: mapMutations.bind(null, namespace),
    mapActions: mapActions.bind(null, namespace)
  }); };

结语

​ 辅助函数为我们使用vuex提供了很多的方便,但是在vue2和vue3中的使用方式有点差别,其实也还好吧。不很难,多熟悉几遍就好了。

​ 如果上面有不对的地方,请喷,我虚心接受。

你可能感兴趣的:(vue3)