Front-end basic direction ~ starting from the project to encapsulate tool functions

Continue to create, accelerate growth! This is the Nth day of my participation in the "Nuggets Daily New Plan · October Update Challenge", click to view the details of the event

foreword

At the beginning of the project, there are often "big guys" in the group to build or transform the front-end project framework used by a project for us, which usually include some commonly used libraries and functions: such as router routing, store data sharing, login and Exception pages, dynamic routing and button permission handling, etc. But in the process of the project, everyone needs to participate! Whether it's a novice or a big guy, when dealing with some basic forms, the basic ideas and code logic should be similar.

Therefore, in order to unify and reuse the code of this part of the function, to reduce the amount of code and reduce the time it takes for "little whites" to write code, we will create a Utils tool function directory to store all the tools we extracted. function.

So how to extract and encapsulate a utility function? We need to start with these steps below.

1. Initial logical abstraction

This part usually happens when we just realize that the piece of code we are writing now might be used in the future, and we will think of the way to deal with it.

Take an example in my project as an example: there is a large dictionary object that stores all the field arrays defined by the front and back ends; but when the details page is displayed, it is generally necessary to find the corresponding label through the value field.

At this time, a function is generally created to be used for searching, or the field array is converted into map format (currently, objects are generally used). We chose the second option at the time:

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;
}, {});
复制代码

Here we have written a function arr2map to convert our dictionary's array of objects into an object of the value: labelformat .

At this point, there is no problem with this logic in the current situation.

2. Having problems

But later, we encountered a new problem: arr2map only supports [{ label: xxx, value: xxx }]the format of , and cannot be used in other scenarios.

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

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. 总结

From the above two scenarios, the tool function is mainly "to process business data to obtain another expected data format" , then "to deal with the same business scenario, or to need a unified display style, etc. , generally use Encapsulate business components to complete.

Data processing (transformation), I personally feel that the most common operation is the operation of arrays or objects, whether it is merging, flattening, etc., in the process of writing such tool functions, not only can we increase our attention when writing tool functions. Understanding can also increase our mastery of algorithms and functional programming.

Wonderful past

Guess you like

Origin juejin.im/post/7150321895970177060