前端业务常量的快捷使用方案:消除恶心的find与魔法值

·  阅读 1225

在前端开发时,经常会使用到业务常量

如下:

const fieldType = [
  { value: 1, label: '文本' },
  { value: 2, label: '数字' },
  { value: 3, label: '视频' },
]
复制代码

使用它的场景大致有三类:

1. 当做表单输入的可选项被遍历

作为select、radio、checkbox等的选项值

<el-select v-model="type">
  <el-option
    v-for="item in fieldType"
    :key="item.value"
    :label="item.label"
    :value="item.value"
  >
  </el-option>
</el-select>
复制代码

这是常量最普遍被使用的方式,所以大多数常量都被存储为数组,并且包含valuelabel两个字段

2. 把value转为label显示

有时后端提供的数据的是value,那么前端显示时需要把数字转化为文字。这个过程在实践中,是很重复、繁琐的:

enumsFilter(value) {
  if (!value) {
    return ''
  }
  const item = fieldType.find(item => item.value === value)
  return item ? item.label : ''
}
复制代码

上述逻辑大概率会在filter中实现(vue3已取消filter功能),也可能出现在其他函数中。

NOTE:最重要的是,这段逻辑无处不在,很恶心。

3. 用英文名获取value,消除魔法值

if(typeValue === 1){
// do something
}
复制代码

魔法值的问题很明显:没有语义,不易于维护。

于是就出现了使用英文名字来获取value的方式,使代码语义化

if(typeValue === fieldTypeEnum.TEXT){
    // do something
}
复制代码

那么就需要数据原来维护,英文名和value的对应关系

fieldTypeEnum = {
    TEXT: 1,
    NUMBER: 2,
    VIDEO: 3,
    ...
}
复制代码
NOTE:一个常量,需要维护两份数据。因为耗费精力,偷懒就造成魔法值泛滥。

上述的后两种使用场景,都有各自的问题。于是我尝试简化这两个场景的代码。

网上的解决方案:

对于上述常量使用场景的相应问题,网上也有一些解决方案:

数据源维护value、label、name三个字段,并使用createEnum处理数据源:

export const fieldType = createEnum([
  { value: 1, label: '文本', name: 'TEXT' },
  { value: 2, label: '数字', name: 'NUMBER' },
  { value: 3, label: '视频', name: 'VIDEO' }
])
复制代码

createEnum返回值为数组,会携带两个方法getLableByValuegetValueByName,来覆盖上述的后两种场景:value => labelname => value,使用示例:

fieldType  // list

fieldType.getLableByValue(1) // '文本'

fieldType.getValueByName('TEXT')  // 1
复制代码

我的优化:

我的纠结点在于:有没有另一种方式,能把函数调用省略掉,一是因为写函数名也很麻烦,二是因为使用调用,代码就会有ReferenceError报错的概率

尝试了很多种方式,终于找到了完美的方式,覆盖三种常量的使用场景,并且使用更简洁:

export const fieldType = createEnum([
  { value: 1, label: '文本', name: 'TEXT' },
  { value: 2, label: '数字', name: 'NUMBER' },
  { value: 3, label: '视频', name: 'VIDEO' }
])


fieldType()  // list

fieldType(1)  // '文本'
fieldType(1, 2, 3)  // '文本、数组、视频'  代码未实现

fieldType('TEXT')  // 1
复制代码

createEnum返回的是函数,通过传参来获取不同的数据,类似于枚举的效果。

优势:

  1. 省略了value ==> label过程中,恶心、重复的find过程;
  2. 三种场景的使用方式都是函数调用,一致且简洁;

代码实现很简单:

/**
 * 业务常量的使用方式扩展
 * @param {Array} source 
    [
      { value: 1,label: '空间站',name: 'SPACE' },
      { value: 2, label: '实践任务', name: 'PRACTICE' },
      { value: 3, label: '其他系统', name: 'OTHERS' }
    ]
  @returns {Function} fn
    fn() ===> source
    fn(value) ===> label
    fn(name) ===> value
 */
export default function createEnum(source) {
  const enumMap = new Map()
  source.map(item => {
    enumMap.set(item.name, item.value)
    enumMap.set(item.value, item.label)
  })

  return function(key) {
    if (key !== undefined) {
      return enumMap.get(key) || ''
    } else {
      return source
    }
  }
}
复制代码

考虑到不是所有的常量都会有第三种使用场景(name=>value),所以name是可选的。你可以碰到此场景时,再回来定义name,以避免多余的工作量,毕竟起英文名也很费精力。

当时想了很多种方式,把source数组和sourceMap结合在一起,代理proxy、迭代器iterator、原型prototype等等,最后选用了函数调用的方式,简单、有效。

懒人提效系列:

面对重复的代码、逻辑,如何提高开发效率

使用类名,高效快捷的进行flex布局

element-ui 弹窗组件封装 极简方案

分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改