写点什么

使用 JS 转换数据的最佳实践

作者:夏木
  • 2022-12-01
    湖南
  • 本文字数:1690 字

    阅读完需:约 6 分钟

使用 JS 转换数据的最佳实践

在开发中,不同输入输出所存储和使用数据总会存在差异,或是数据类型、字段经常出现偏差。


当前后端分离开发时,服务端返回的数据与前端渲染时所定义的数据,即使是相同的业务逻辑,也会存在差异。又或者使用多个开源组件时,相互间需要传递的数据类型定义也会有差异。


但是,在相同的业务场景,这种数据的差异又仅仅是表象的差异,比如:字段键名不同,值的数据类型不同。


通过开发经验归纳,有下面三种差异:


  1. key 名称不一致

  2. value 类型不一致

  3. 数据结构不一致:输入是 {a,b,c},输出是 {a,b}{a,b,c,d}


当然,实际开发中还存在着更复杂的差异,那样的情状建议特例化处理。


而造成这种情况的原因通常是开发协作时沟通不够详尽,或是开源组件缺乏统一的规范。为了简化处理这种情况的流程,更合适的实践方案是在前端通过数据转换函数进行归一化处理。


针对上述数据差异,我开源了数据转换库 d-pipe

使用

import Pipe from 'd-pipe';
const data = { a: 'a', b: 'b' };const pipe = new Pipe(data);pipe .add('c', () => 'c') .delete(['b']) .editValue('a', () => 'aa');pipe.data; // { a: 'aa', c: 'c' }
复制代码


Pipe 实例化导入数据 data,再通过可链式调用的转换方法 adddelete 等对数据进行转换。


Tips:传入的数据必须是非空对象和非空数组,{} 和 [] 都是不允许的数据。开发者必须在业务中明确判断数据是非空的,才能传递给 Pipe。

否则 Pipe 将抛出一个类型错误: The dataSet must be an Object or Array,and cannot be an empty object or empty array.


关于所有转换方法的介绍和使用请查看 README

特征

  1. immutable:内部使用 cloneDeep 函数,对传入的数据进行深拷贝,所有修改不影响原始数据


   constructor(data: any) {   this.originalData = deepClone(data);   this.result = deepClone(data);   }
复制代码


  1. 方法调用顺序无关:使用链式调用方法时无需关心代码书写顺序,内部始终使用固定的执行顺序(具体顺序查看 README)。


   // 测试用例   test('valueDelivery', () => {     const data = { a: 'a', b: 'b' };     const pipe = new Pipe(data);     pipe       .add('c', () => 'c')       .delete(['b'])       .editValue('a', () => 'aa');     expect(pipe.data).toEqual({ a: 'aa', c: 'c' }); // pass     // 顺序无关     pipe       .editValue('a', () => 'aa')       .add('c', () => 'c')       .delete(['b']);     expect(pipe.data).toEqual({ a: 'aa', c: 'c' }); // pass   });
复制代码


  1. 归纳操作:Pipe 内部会收集所有调用方法,以便于对数组数据只使用一次遍历完成数据操作


   // 源码:src/core/pipe.ts   private convert() {   // NOTE: 判断是否是数组   if (Array.isArray(this.originalData)) {     this.result = this.originalData.map((item, index) => {       // NOTE: 当是最后一条数据时,使用 forEachOnce ,来清除记录,从而不重复计算       const type =         index === (this.originalData as T[]).length - 1           ? 'forEachOnce'           : 'forEachAlways';       return this.convertItem(item, type);     });   } else {     this.result = this.convertItem(this.originalData, 'forEachOnce');   }   if (this.noExitKeys.size > 0) {     console.warn(       `[warn]:Fields of ${[...this.noExitKeys].join(',')} do not exist.`,     );   }   }
复制代码


  1. 链式调用:使用链式调用方法,使代码更有组织性,阅读性更佳,更便于维护。

  2. 类型描述:d-pipe 完全使用 typescript 编写,具有完备的类型描述。

示例

  • 提交表单时删除空数据:null, undefined, ''


  const data = { price: 100, discount: undefined };  const pipe = new Pipe(data);  pipe.clean([undefined, null, '']);  console.log(pipe.data); // {price:100}
复制代码


  • 渲染数据时保留不同字段


  const data = { name: 'Jane', vip: '100', gender: 'male' };  const pipe = new Pipe(data);  pipe.pick(['name', 'gender']);  console.log(pipe.data); // {name:"Jane",gender:"male"}
复制代码


发布于: 刚刚阅读数: 4
用户头像

夏木

关注

还未添加个人签名 2019-01-16 加入

还未添加个人简介

评论

发布
暂无评论
使用 JS 转换数据的最佳实践_typescript_夏木_InfoQ写作社区