創造し続け、成長を加速!「ナゲッツデイリー新プラン・10月アップデートチャレンジ」参加N日目です、クリックしてイベントの詳細をご覧ください
序文
プロジェクトの開始時には、プロジェクトで使用されるフロントエンド プロジェクト フレームワークを構築または変換する「大物」がグループにいることがよくあります。これには通常、一般的に使用されるライブラリと機能が含まれます。たとえば、ルーター ルーティング、ストアデータ共有、ログインおよび例外ページ、動的ルーティング、ボタン権限の処理など。しかし、プロジェクトの過程では、誰もが参加する必要があります! 初心者であろうと大物であろうと、いくつかの基本的なフォームを扱う場合、基本的な考え方とコード ロジックは似ているはずです。
したがって、関数のこの部分のコードを統合して再利用するために、コードの量を減らし、「小さな白人」がコードを書くのにかかる時間を短縮するために、Utilsツールの関数ディレクトリを作成して、すべての関数を格納します。ツールが抽出した機能。
では、ユーティリティ関数を抽出してカプセル化する方法は? 以下の手順から始める必要があります。
1. 初期の論理的抽象化
この部分は通常、現在書いているコードの一部が将来使用される可能性が高いことに気付いたときに発生し、それに対処する方法を考えます。
例として私のプロジェクトの例を見てみましょう: フロント エンドとバック エンドによって定義されたすべてのフィールド配列を格納する大きなディクショナリ オブジェクトがありますが、詳細ページが表示されると、通常は値から対応するラベルを見つける必要があります。分野。
このとき、一般的には関数を作成して検索に使用するか、フィールド配列をマップ形式に変換します (現在はオブジェクトを使用するのが一般的です)。その時点で 2 番目のオプションを選択しました。
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. 总结
上記の 2 つのシナリオから、ツールの機能は主に「ビジネス データを処理して別の期待されるデータ形式を取得する」、次に「同じビジネス シナリオを処理する、または統一された表示スタイルを必要とする」などであり、一般的に Encapsulate ビジネス コンポーネントを使用します。完了します。
データ処理 (変換)、個人的には、最も一般的なのは配列またはオブジェクトの操作であり、マージ、フラット化などであると感じています。このようなツール関数を作成する過程で、ツールを作成するときに注意を払うことができるだけではありません。関数を理解することで、アルゴリズムと関数型プログラミングの習熟度も向上します。