最近自己一直在学习vue3,学习到了很多的新知识,非常的感谢coderwhy老师的指导。由于自己一直有做笔记这个习惯,学习到了vuex中的辅助函数,分别在vue2和vue3中的分别使用,以前感觉自己有必要记录下来,来对自己加深印象,巩固基础。
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中推出了辅助函数,来方便我们解决此类的问题。
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() {}
}
上面说了这么多,其实总结出来就两句话:
辅助函数返回一个对象,对象的形式是 key是字符串, value是函数
内部是实现使用this.$store来得到store中的值 (这里主要是在vue3中的setup时,要注意,setup中没有this)
辅助函数,就两种形式的写法,对象形式和数组形式
mapState一般在计算属性中使用
...mapState(['name', 'age']) //数组形式
...mapState({ //对象的形式
name: state => state.name,
age: state => state.age
})
mapGetters也是一般在计算属性中使用
...mapState(['translateName', 'translateAge']) //数组形式
...mapState({ //对象的形式
translateName: 'translateName',
translateAge: 'translateAge'
})
他们的使用方法和mapGettets的方式一样的,只不过在他们是在methods中使用
...mapMutations(['changeName', 'changeAge']) //数组形式
...mapMutations({ //对象的形式
changeName: 'changeName',
changeAge: 'changeAge'
})
...mapActions(['asyncChangeName', 'asyncChangeAge']) //数组形式
...mapActions({ //对象的形式
changeName: 'asyncChangeName',
changeAge: 'asyncChangeAge'
})
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
}
}
是不是,写的很难受啊。是不是又回到最开始的疑问啦? 当数据变多的时候, 又会大大的增加代码量。所以,我们还是需要借助辅助函数。
在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
}
}
是不是又可以愉快的写代码啦!
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基本是一样的。
//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
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
}
}
}
在上面,我们辅助函数封装了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中的使用方式有点差别,其实也还好吧。不很难,多熟悉几遍就好了。
如果上面有不对的地方,请喷,我虚心接受。