前端为什么要使用枚举
-
- 一、枚举的业务场景与遇到的问题
- 二 、如何改善?
- 三、实现
一、枚举的业务场景与遇到的问题
我们在日常业务开发中,经常遇到枚举,如商品状态、页面状态、审核状态等,翻阅以往的一些业务代码,会发现很多地方都是这么写的:
<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>
如此一来,具有以下好处:
- 去魔数化:后端假如改了审核中状态为2,那么我们就只需要在开头把0改为2即可
- 语义化:通过STATUS.AUDITING我们就可以大概猜出含义
- 定义一体化:枚举值和枚举描述写在了一起,不分散
- 使用方便:无需额外的过滤器,就可以通过枚举名称/枚举值获得枚举含义
三、实现
实现代码如下:
/** * 枚举定义工具 * 示例: * 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] || '' } } }