我们在日常业务开发中,经常遇到枚举,如商品状态、页面状态、审核状态等,翻阅以往的一些业务代码,会发现很多地方都是这么写的:
<span v-if="status == 0">审核中</span>
<span v-else-if="status == 1">审核通过</span>
以上代码其实存在以下问题:
魔数化:一旦有个数值改动,就得全局替换匹配
差语义化:无法直观通过值推导出含义 于是,进阶做法我们想的是通过引入常量,如:
<span v-if="status == STATUS.AUDITING">审核中</span>
<span v-else-if="status == STATUS.PASS">审核通过</span>
然而,我们其实还会遇到以下场景:
需要拿到枚举值获得枚举含义 特别是在列表中尤其常见。我们通常的做法是:建立过滤器,建立Map,如:
export default {
// ...
filters: {
statusName: status => {
const map = {
[STATUS.AUDITING]: '审核中',
[STATUS.PASS]: '审核通过'
}
return map[status] || ''
}
}
}
这样子虽然也挺方便,但是仍然不够完美:
定义隔离:枚举值和枚举含义分离,还是会带来一定维护上的问题
我们期望的应该是定义能够一体化,而不是分散化。参考Java里的枚举做法,其实好很多:
public enum AgingTypeEnum {
ALL(0, "全部"),
SPECIAL(1, "特批时效"),
PLATFORM(2, "平台定义");
// ...
}
但是,虽然TS里也实现了enum,但其实做法还是有些不一样,还是不那么利于我们的业务场景。因为TS里的enum,也不是枚举值与含义定义一体化。对于我们的业务场景,可能下面这么做更利于我们的使用:
const STATUS = createEnum({
AUDITING: [0, '审核中'],
PASS: [1, '审核通过']
})
export default {
// ...
created() {
this.STATUS = STATUS
}
}
审核中 审核通过
<p>当前状态:{STATUS.getDescFromValue(syncData.status)}</p>
<p>也可用通过枚举名称获取描述:{STATUS.getDesc('AUDITING')}</p>
如此一来,具有以下好处:
实现代码如下:
/**
* 枚举定义工具
* 示例:
* const STATUS = createEnum({
* AUDIT_WAIT: [1, '审核中'],
* AUDIT_PASS: [2, '审核通过']
* })
* 获取枚举值:STATUS.AUDIT_WAIT
* 获取枚举描述:STATUS.getDesc('AUDIT_WAIT')
* 通过枚举值获取描述:STATUS.getDescFromValue(STATUS.WAIT)
*
*/
interface EnumDefinition {
[enumName: string]: [number, string]
}
export default function createEnum(definition: EnumDefinition) {
const strToValueMap = {}
const numToDescMap = {}
for (const enumName of Object.keys(definition)) {
const [value, desc]: [number, string] = definition[enumName]
strToValueMap[enumName] = value
numToDescMap[value] = desc
}
return {
...strToValueMap,
getDesc(enumName: string): string {
return definition[enumName] && definition[enumName][1] || ''
},
getDescFromValue(value: number): string {
return numToDescMap[value] || ''
}
}
}
非TS版本
export default function createEnum(definition) {
const strToValueMap = {}
const numToDescMap = {}
for (const enumName of Object.keys(definition)) {
const [value, desc] = definition[enumName]
strToValueMap[enumName] = value
numToDescMap[value] = desc
}
return {
...strToValueMap,
getDesc(enumName) {
return definition[enumName] && definition[enumName][1] || ''
},
getDescFromValue(value) {
return numToDescMap[value] || ''
}
}
}