layui数据表格_默认值+TS类型约束提高数据处理成功率

我们在处理数据时,常常会遇到某项数据或者属性是 undefined,从而引起中断性错误,造成数据处理失败。解决这一问题最直接的办法就是在使用前判断是否 undefined ,但是如果每一个数据使用前都进行判断,非常繁琐,而且容易遗漏。所以这里给大家介绍两个办法:

  • 利用默认值解决 undefined,避免繁琐的判断过程;

  • 利用 TypeScript 的类型系统对数据进行检查,避免遗漏。

为了更直观的描述问题和解决办法,我们设计了这样一个需求:

需求:后端响应数据转换成 UI 所需要的数据

一般来说,现在的前端组件对表格数据的要求,都是:

  • 以数组表示数据行,即当前页数据

  • 每行是一个数据对象,其属性对应于表格列

  • 除数据行之外,还包含一些附加信息用于分页,比如

    • 库存数据总数(不是当前数据行的总数)

    • 每页数据呈现条数(一般由前端传给后端,后端返回数据时带出来)

    • 当前页数(一般由前端传给后端,后端返回数据时带出来)

那么,从后端返回的数据可能会是这样(JSON 示例)

 {  "code": 0,  "message": "查询成功",  "data": {    "total": 12345,    "page": 3,    "rows": [      {        "id": 34,        "title": "第 34 号数据",        "stamp": "2020-06-25T20:18:19Z"      },      ...    ]  }}

前端呈现如果使用 Layui 的数据表格,它所要求的数据格式是这样的(来自官方文档):

 {  "code": 0,  "msg": "",  "count": 1000,  "data": [{}, {}]}

远程响应数据结构和表格需要的数据结构之间,对应关系非常明显。

我们用 res 来表示后端 JSON 转成的 JavaScript 对象,那么为 Layui 准备的数据会这样取值:

 const tableData = {    code: res.code,    msg: res.message,    count: res.data.total,    data: res.data.rows}

这个处理转换非常简单,只是做了一个属性名称的变化。

但是远端返回的数据,可能没有 code,或者 message,甚至没有 data,那么上面的处理结果就可能包括 undefined。前端组件不希望有这样的值,所以需要添加默认值处理。

利用解构变量可以赋予默认值这一特性

为了演示默认值处理,我们假设,后端返回的数据规范比较灵活,为了节约网络资源,有一个默认值约定:

  • 如果请求正常完成,省略 "code": 0

  • 如果没有特殊消息,省略 "message": ""

  • 如果没有数据,即 "total": 0 的情况,省略 "data": {}

  • 如果当前页没有数据,省略 "rows": []

这种情况下,在进行数据转换时就需要充分考虑到某项数据可能不存在,避免 undefined 错误。

Object.assign() 或者 Spread 运算符可以部分解决这个问题,但是

  • 只能解决单层(直接)属性,不能解决深层属性默认值

  • 有坑,它们对 “undefiend 属性”和“不存在的属性”处理行为不同

不过我们可以利用解构变量能够赋予默认值的特性来进行处理。下面这段代码就巧妙地利用了这一特性来避免 undefined 错误。

 function getData(res) {    const { code = 0, message: msg, data = {} } = res;    const { total: count = 0, rows = [] } = data;    return {        code,        msg,        count,        data: rows    }}const tableData = getData(res);

解构确实是可以解决问题,但是如果遗漏或者写错属性,调试起来恐怕不易。比如上面

 const { message: msg } = res;

就很容易错写成

 const { msg } = res

这是 JS 的硬伤,即使用 ESLint 这样强悍的静态检查工具也不能解决。但是如果引入强类型定义,使用 TypeScript 就好办了。

使用 TypeScript 和类型检查转换过程

既然用 TypeScript,就需要为两个数据结构定义对应的类型了。

我们有两个数据结构 restableData,在 TypeScript 里可以直接把它们定义为 any 类型,这是最简单的操作,但是没什么用 —— 因为 TypeScript 不检查 any 类型。

所以先根据我们之前的约定,把 restableData 的类型定义出来:

 interface FetchData<T> {    total: number;    page?: number;    rows?: T[];}interface FetchResult<T> {    code?: number;    message?: string;    data?: FetchData<T>;}interface LayuiTalbeData<T> {    code: number;    msg?: string;    count: number;    data: T[];}

然后要把 res 声明成 FetchResult 类型。这里我们暂时不关心具体每行数据的结构,所以直接用 any 代替好了

 const res: FetchResult<any> = await fetch();// 或者// const res = await fetch() as FetchResult;

这种情况下,假如我们不小心写错了属性名,比如解构时把源属性 message 错写成了目录属性名 msg,即 const { msg } = res,VSCode 是会提示错误的:

5c1cb64633be6887f247144027121794.png

解决的办法是使用解构重命名:

 const { message: msg } = res;

或者,如果我们忘了处理 undefined,比如忘了给解构的 rows 赋予初始值,那也会得到错误提示,因为

  • 源数据定义中 rows?: T[] 表示它可省略,即可能是 undefined

  • 目标数据定义中 data: T[] 表示它一定不会是 undefined

27217fc25d2ae3585306025b6405c428.png

解决的办法是,赋予初始值,使其不可能为 undefined

 const { rows = [] } = data;

完整的 TypeScript 代码如下(类型定义参考前面的定义):

 function getData<T>(res: FetchResult<T>): LayuiTalbeData<T> {    const { code = 0, message: msg, data = {} as FetchData<T> } = res;    const { total: count = 0, rows = [] } = data;    return {        code,        msg,        count,        data: rows    }}// 这里 TypeScript 可以推导出 tableData 类型是 LayuiTalbeDataconst tableData = getData(res);

使用 Optional Chain 和 Nullish Coalescing

回到 JavaScript,其实还有一个办法可以处理默认值的问题:

 const tableData = {    code: res.code || 0,    msg: res.message || "",    count: (res.data && res.data.total) || 0,    data: (res.data && res.data.rows) || []}

这也是一个非常常见的办法。这个办法在 TypeScript 中配置类型定义同样可行。只是对多层属性的处理仍然显得有点麻烦。不过 JavaScript 最近引入了“Optional Chain”和“Nullish Coalescing”特性,这个代码可以更简洁:

 const tableData = {    code: res.code ?? 0,    msg: res.message,    count: res.data?.total ?? 0,    data: res.data?.rows ?? []};

放在 TypeScript 中是这样写的:

 const tableData: LayuiTalbeData<any> = {    code: res.code ?? 0,    msg: res.message,    count: res.data?.total ?? 0,    data: res.data?.rows ?? []}

上面的 TypeScript 代码中,如果错写了 res.msg 或者忘了加 ?? [] 等,TSC 或者 VSCode 都会有错误提示,以保证你能修正代码。

看,新特性配合 TypeScript 的强类型检查,简直是完美!

小结

我们讲了最简单的数据转换:直接按源数据属性取值。虽然简单,但是有坑,也有处理的方法和技巧:

  • 注意可能出现的 undefinednull,甚至 NaN 等需要特殊处理的数据

  • 使用解构将属性提取出来,并根据数据结构的需要适当赋予初始值

  • 使用 Optional Chain 和 Nullish Coalescing 简化对可能为 undefined 属性的处理

  • 使用 TypeScript 在开发期检查错误

这里讲的数据处理比较基础,但其中的坑也比较容易被忽略。后面在专栏或订阅号中,我们还会继续探讨更复杂一些的数据处理分析方法和处理技巧,请关注!


喜欢此文,点个在看 ⇘

支持作者,赏个咖啡豆 ⇓

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值