内容简介:我们在处理数据时,常常会遇到某项数据,或者属性是为了更直观的描述问题和解决办法,我们设计了这样一个需求:一般来说,现在的前端组件对表格数据的要求,都是:
我们在处理数据时,常常会遇到某项数据,或者属性是 undefined
,从而引起中断性错误,造成数据处理失败。解决这一问题最直接的办法就是在使用前判断是否 undefined
,但是如果每一个数据使用前都进行判断,非常繁琐,而且容易遗漏。所以这里给大家介绍两个办法
undefined
为了更直观的描述问题和解决办法,我们设计了这样一个需求:
需求:后端响应数据转换成 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,就需要为两个数据结构定义对应的类型了。
我们有两个数据结构 res
和 tableData
,在 TypeScript 里可以直接把它们定义为 any
类型,这是最简单的操作,但是没什么用 —— 因为 TypeScript 不检查 any
类型。
所以先根据我们之前的约定,把 res
和 tableData
的类型定义出来:
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<T>
类型。这里我们暂时不关心具体每行数据的结构,所以直接用 any
代替好了
const res: FetchResult<any> = await fetch(); // 或者 // const res = await fetch() as FetchResult<any>;
这种情况下,假如我们不小心写错了属性名,比如解构时把源属性 message
错写成了目录属性名 msg
,即 const { msg } = res
,VSCode 是会提示错误的:
解决的办法是使用解构重命名:
const { message: msg } = res;
或者,如果我们忘了处理 undefined
,比如忘了给解构的 rows
赋予初始值,那也会得到错误提示,因为
- 源数据定义中
rows?: T[]
表示它可省略,即可能是undefined
- 目标数据定义中
data: T[]
表示它一定不会是undefined
解决的办法是,赋予初始值,使其不可能为 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 类型是 LayuiTalbeData<any> const 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 的强类型检查,简直是完美!
小结
我们讲了最简单的数据转换:直接按源数据属性取值。虽然简单,但是有坑,也有处理的方法和技巧:
- 注意可能出现的
undefined
、null
,甚至NaN
等需要特殊处理的数据 - 使用解构将属性提取出来,并根据数据结构的需要适当赋予初始值
- 使用 Optional Chain 和 Nullish Coalescing 简化对可能为
undefined
属性的处理 - 使用 TypeScript 在开发期检查错误
这里讲的数据处理比较基础,但其中的坑也比较容易被忽略。后面在专栏或订阅号中,我们还是继续探讨更复杂一些的数据处理分析方法和处理技巧,请关注!
请关注公众号 边城客栈 ⇗
看完了先别走,点个 赞 ⇓ 啊, 赞赏 ⇘ 就更好啦!
以上所述就是小编给大家介绍的《默认值+TS类型约束提高数据处理成功率》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 面试前如何准备才能提高成功率
- Java程序员这样面试,成功率提升90%
- 爱奇艺 iOS 移动端网络优化实践(请求成功率优化篇)
- 吃透这套Java面试题,拿offer成功率再翻一番
- 程序员面试时这样介绍自己的项目经验,成功率能达到98.99%
- Python数据处理(二):处理 Excel 数据
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。