定义枚举
在绝大多数项目中定义枚举都是用数组加对象的形式
const sex = [
{
label: '男',
value: 0
},
{
label: '女'
value: 1
}
]
这种方式在大部分的ui框架中可以作为选择器或其他表单控件的数据来源,有足够的扩展性。如果有个将“男”的文字颜色改为蓝色,”女“改为红色的需求,只需要再加个字段就能实现。
const sex = [
{
label: '男’,
value: 0,
color: 'bule'
},
{
label: '女'
value: 1,
color: 'red'
}
]
映射枚举
使用上面的方式定义枚举唯一不优雅的地方就是在映射枚举的label上,在项目中映射枚举的方式是真的多。
比如在直接在模板中直接v-if判断
<span v-if="row.sex === 0"> 男 </span>
<span v-if="row.sex === 1"> 女 </span>
使用上面这种方式在新增加一个性别时 ,不仅要在定义的枚举上加,还要在模板中在加一个v-if,这无疑是给项目增加了更多的心里负担!
比较好的是在数据的对象上增加一个新的字段,如:sexLabel ,将枚举的label赋值给 sexLabel 然后在到模板中使用。后续即使有新的属性也只需要在定义一个新的值。
<template>
<span> {{ row.sexLabe }} </span>
</template>
<script setup>
cosnt row = { sex; 0 }
let sexLabel = ’-‘
sex.foreEace(item => {
if (itme.value === row.sex) {
row.sexLabel = item.label
}
})
</script>
除了foreace还可以使用find方法,只需要一行代码就行,在长达一年的时间中就是使用find的方式映射枚举,用着用着发现重复的代码还是有点多。
row.sexLabel = sex.find(item => item.value === row.sex)?.label || ''
之后尝试封装成公用函数,虽然代码量是少了,但每次都要映入这个公共函数,还是不够优雅!
/**
* 获取状态对应的标签
* @param enums 状态枚举列表
* @param val 值
* @returns
*/
export function getLabelByValue(enums, val) {
const data = enums.find((ele) => ele.value === val)
if (data) {
return data.label
}
return '-'
}
row.sexLabel = getLabelByValue(sex, row.sex)
我的最佳实践
思考
find
方法重复的代码多,封装公共方法每次都要引入,特别部分枚举在多个页面同时使用时,不仅要引入枚举,还要引入对应的方法,增加了心里负担。
为了解决这部分问题,我想了很多方案。最好的方式是在枚举本身可以有个根据对应的值映射对应的label
的方法,这是最好的方式!
row.sexLabel = sex.getLabelByValue(row.sex)
定义枚举的方式是数组,数组本身是没有这个方法,如何在这个枚举数组上增加这个方法?在数组的原型上添加肯定是不行的,那相当于所有定义的数组都会拥有该方法,这个方法应该是枚举独有的!
所以如何单独给枚举数组增加这个方法是主要的问题!回忆自己所学的知识,添加私有方法应该是面向对象的知识,js面向对象这一块的知识,只是在某站看过视频,没有深入了解过,本身工作这一年多也是使用vue开发,react也只是听过的情况下。如果是枚举是用对象还好,数组虽然也是对象,但完全无法下手,就此卡住。
而自己的技术菜,没看过几本书,每天写业务,没啥技术沉淀,相信看到这的,基本已经想到如何在枚举数组上定义私有方法了吧。
解决方案
之后在看js红宝书时,我找到了如何在枚举数组添加这个私有方法的解决办法!那就是继承后扩展!
es6的class语法解决了使用构造函数很多问题,其中就包括继承!es6使用extends可以继承内置类型,就可以不影响数组本身,只在继承的新类型添加私有方法的能力!
定义一个 EnumArray 的新类型,在添加 getLabelByValue 方法,就实现了我想要的效果。
class EnumArray extends Array {
/** 根据值获取标签 */
getLabelByValue(val) {
return this.find((item) => item?.value === val)?.label || '-'
}
}
使用 new EnumArray()
来创建这个枚举,然后sex得到就是最开始定义的那个sex枚举数组了
const sex = new EnumArray(
{
label: '男,
value: 0
},
{
label: '女'
value: 1
}
)
映射枚举的方式就可以链式调用 getLabelByValue
方法,传入对应值就可以获取到对应的label
。
row.sexLabel = sex.getLabelByValue(row.sex)
除了getLabelByValue
方法,还可以添加其他方法,比如根据值获取整个对应枚举对象的方法。
getEnumObjByValue(value) {
return this.find(item) => item?.value === val)
}
最后,直接使用new EnumArray
的方式会造成一点小小的误解,所以可以写个 createEnum
函数,接收一个数组的方式创建枚举。
function createEnum(enums) {
return Object.freeze(new EnumArray(....enums)
}
使用方法
const sex = createEnum([
{
label: '男',
value: 0
},
{
label: '女'
value: 1
}
])
Object.freeze
这个方法是冻结这个枚举数组,在当把枚举定义在vue2的data时,可以让vue不监听这个枚举的变化,因为vue会重写data中的数组方法,所以使用vue并且将枚举放置在data中必须保留Object.freeze
。其他场不会重写原始类型的场景也可以省略Object.freeze
方法。
以上就是目前在项目中使用枚举的方式了,如果有更好更优雅的方式,可以分享,一起交流,一起进步
踩的坑
老项目大部分的枚举都是定义在 .vue
文件上,在多个页面都是直接复制粘贴获取,导致每次一改要改好多地方,一不小心就改漏了,后面写的枚举都是放在 utils/enumeration.js
文件里,哪里用了就直接引用。
枚举的命名上也踩过坑,之前枚举都是对应后端返回的字段命名,但将枚举放在一个文件中就会出现命名重复的问题,比如状态 state
好多地方都是这个名字,但是内容不一样,可以加个前缀,如订单页面,就加个订单的前缀,末尾加上Enums的后缀,表示这是个枚举。
在翻看老代码时,有部分常用的枚举都是定义在vuex上,当时我啥不懂,又不想看老代码,没发现。。。
不过我建议最好不要将枚举放置在vuex这样的状态管理上,毕竟枚举时静态的,不需要管理状态吗,如果要放在vuex上,最好使用Object.freeze
包裹,不然大量的枚举都会被监听,对项目的影响是很大的。