持续创作,加快生长!这是我参与「日新方案 10 月更文应战」的第N天,点击检查活动详情

前语

在项目开端初期,往往都是有组里的“大佬”为咱们搭建或许说改造一个项目使用的前端项目结构,通常都会包括一些常用的库和函数:比方 router 路由、store 数据共享、登录和异常页、动态路由和按钮权限处理等等。但是在项目进行过程中,都需求咱们一同参与!不管是小白也好、大佬也好,在处理一些基础的表单表格的时分,基本思路和代码逻辑应该都是差不多的。

所以为了一致和复用这部分功能的代码,来削减代码量、下降“小白”们编写代码时会花费的时刻,咱们都会创建一个 Utils 东西函数目录,用来存放咱们抽离出来的一切东西函数。

那么如何抽离和封装一个东西函数呢?咱们需求从以下这些步骤开端。

1. 初始的逻辑抽离

这一部分一般都是发生在咱们刚刚意识到咱们现在编写的这一段代码在今后我或许还会用到的时分,咱们会想到的处理方式。

以我项目中的一个例子来说:有一个很大的字典目标,存放了一切前后端界说的字段数组;但是在详情页显现的时分一般都需求通过 value 字段来查找对应的 label。

此刻一般都是树立一个函数专门用来查找,或许将字段数组转为 map 格局(现在一般都是用目标)。咱们其时挑选了第二种方案:

export const Enums = {
  protocolType: [
    { label: "label 1", value: 1 },
    { label: "label 2", value: 2 },
    { label: "label 3", value: 3 }
  ],
  nodeType: [
    { label: "label 1", value: 1 },
    { label: "label 2", value: 2 }
  ]
}
export function arr2map (arr) {
  return arr.reduce((mmap, item) => {
    mmap[item.value] = item.label;
    return mmap;
  }, {});
}
export const EnumsMap = Object.keys(Enum).reduce((eEmap, groupName) => {
  eEmap[groupName] = arr2map(Enum[groupName]);
  return eEmap;
}, {});

这儿咱们编写了一个函数 arr2map ,用来将咱们字典的目标数组转化为一个 value: label 格局的目标。

此刻在当前状况下这段逻辑是没有一点问题的。

2. 遇到问题

但是在后来,咱们遇到了一个新问题:arr2map 只支撑 [{ label: xxx, value: xxx }] 的格局,不能用于其他场景。

所以,咱们又对其进行了一次改造(当然,改造必须影响最小化,即不会影响以前的代码,也不需求重新对以前的代码进行修正)

export function notNull(val) {
  return val !== undefined && val !== null;
}
export function arr2map(arr = [], props = { label: "label", value: "value" }) {
  return arr.reduce((mmap, item) => {
    mmap[item[props.value]] = notNull(props.label) ? item[props.label] : true;
    return mmap;
  }, {});
}

这时咱们的数组转目标的函数就算是比较成熟了。能够支撑一个装备项来控制咱们从目标数组中提取目标的哪些元素来生成新的目标。

3. 新的调整

上面的 arr2map 函数,我信任也已经习惯了大部分的使用场景。但是,咱们又遇到了一个更难受的问题:不只是需求转成 {value: label} 的状况,还需求得到对应字段的一切特点。

比方 [{label: 'label1', value: 1, disabled: true, author: 'xxx'}] 需求转成 { 1: {label: 'label1', value: 1, disabled: true, author: 'xxx'}}

所以此刻有需求对 arr2map 进行新的改造。

export function notNull(val) {
  return val !== undefined && val !== null;
}
export function arr2map(arr = [], props = { label: "label", value: "value" }, retain = false) {
  return arr.reduce((mmap, item) => {
    if (retain) {
      mmap[item[props.value]] = item
    } else {
      mmap[item[props.value]] = notNull(props.label) ? item[props.label] : true;
    }
    return mmap;
  }, {});
}

4. 其他应战

上面的一部分代码我信任大部分的同学都能正确的处理,或许想到更优秀完善的代码。

但是在后续的项目推动中,又接到了产品的一系列新的需求。其间一个便是:依据一个值从树型数组中拿到一切上级节点的 label 值组成一个完整的字符串路径回来。

这个功能一般在 组织机构树、关系树 等场景中。为了做到后续能兼容更多的场景更多的数据类型,所以然需求接纳一些装备项。终究的代码如下:


/**
 * 获取一个数据在树形数组中对应的称号 ( 场景:依据code在组织树中查询对应的组织称号 )
 * @param { array } tree 包括子节点的数据目标
 * @param { * } value 当前查询的值, 一般是字符串或许数字
 * @param {{key?: string, label?: string, children?: string}} props 默认关键字(key: 查询值键名,label: 称号键名)
 * @param { ?object } options 装备项
 * @return { string | undefined } 称号
 * */
export function getTreeNodeLabel(tree, value, props = {}, options = {}) {
  let { key = "code", label = "label", children = "children" } = props;
  let { splice = true, hideFirst = false } = options;
  for (let node of tree) {
    if (node[key] === value) {
      return node[label];
    }
    if (notEmpty(node[children])) {
      let res = getTreeNodeLabel(node[children], value, props, { splice });
      if (res) {
        if (hideFirst) {
          return res;
        }
        return splice ? `${node[label]}/${res}` : res;
      }
    }
  }
  return undefined;
}

这儿接纳两个装备目标:props 和 options。

  • props: 用来装备数据获取以及承认递归目标,确保能够通过装备这几个参数来习惯多种特点数据格局
  • options:用来装备输出数据格局;splice 承认是否需求分割线,hideFirst 是否需求躲藏尖端节点

后面其实还能够扩展,如果将躲藏尖端节点 label 改为 躲藏几级节点的label,分割线也能够装备,或许支撑用函数来处理等等。

5. 总结

从上面两个场景来看,东西函数主要是 “对业务数据进行处理,得到别的一种预期中的数据格局”,那么 “处理同种业务场景,或许需求一致显现款式等状况,一般都是用封装业务组件来完成。

数据处理(转换),个人感觉最常见的便是对数组或许目标的操作,不管是兼并、扁平化等等,在编写这类东西函数的过程中,不只能够添加咱们对编写东西函数时注意事项的了解、也能添加咱们对算法、函数式编程的掌握。

往期精彩

  • Bpmn.js 进阶攻略
  • Vue 2 源码阅读了解
  • 一行指令实现大屏元素分辨率适配(Vue)
  • 根据 Vue 2 与 高德地图 2.0 的“线面编辑器”