欢迎关注公众号《
前端界
》,文章会同步更新,也可快速加入前端交流群!
问题出现背景
在某个开开心心上班的周五,突然测试老大找过说昨天上线的项目有大问题,数据都不显示了,早餐都来不及吃赶紧找原因。
我们公司页面上的问题一般都是找前端先定位(前端老湿人~),看到直播列表数据返回正常,但是通过直播id获取直播详情时,数据返回为空。
心想一定是后端的幺蛾子了, 于是找后端说明情况:
根据你返回的直播ID查不到详情数据, 一定是你接口有问题!
此时,告一段落,前端完胜,回去继续吃两块钱买的包子~
第二轮PK
屁股刚落座,后端来了,心情也很复杂的样子:
你给的ID 我查了,数据库根本没有这个ID,我看了,这不是我给你的,你那边是不是对数据做处理了
我再次打开浏览器, 查看数据, 我给的就是Network中你返回的数据啊!没有处理啊。双方各执一词,各自怀疑~
问题定位
我发了请求到的数据作为证据:
后端同学给我发了postman返回的数据作为证据:
前端获取到的roomid
:10976458979374928
, 后端返回的:roomid
:10976458979374929
,相差一位,怎么这么奇怪!
既然后端数据没有问题,此时我想难道是我浏览器有缓存问题了,难道真是前端的问题
这轮PK,心生疑惑
为什么后端返回的json数据,到前端就不一样了呢?
怯怯的继续定位!
发现问题
揣着对这两值的思考,我在控制台打印了一下:
终于让我发现了问题所在,我在控制台看到了输出结果居然是一样的
此时瞬间想到了JS中数据精度问题,回想经常看的面试八股文,在JavaScript中,Number类型范围-2^53 + 1 到 2^53 - 1
但是10976458979374928
也明显超出了数据范围,为什么它打印正常,我又尝试了一下10976458979374927
:
发现输出的也是10976458979374928
这个值, 说明超出了都有问题,只是刚好这个值输入一致。
其实 ES6 引入了Number.MAX_SAFE_INTEGER
和Number.MIN_SAFE_INTEGER
这两个常量,用来表示这个范围的上下限。
Number.isSafeInteger()
用来判断一个整数是否落在这个范围之内。
可以看到这几个值其实都是超出JS的Number范围的,看到的输出可能会有精度丢失问题的。
为啥后端的数据没有问题呢?
在Java中Long类型的取值范围是-2^63 + 1 到 2^63 - 1
, 比JavaScript中大很多,所以后端能正常处理。
解决办法
既然定位到了问题,那怎么解决了,于是又找到了后端,说明情况。后端给了两个解决方案:
- 方式1: 数据库中存的就是数值型,修改数据库存的类型为字符串
- 方式2:返回接口时转为字符串类型给前端
因为线上问题,客户群里已经炸锅了, 临时先采用了方式2来解决, 后期后端在进行方式1的变更。
我们后端还是很好说话的,如果遇到不配合的后端,那前端应该怎么来处理呢? 作为有积极向上思想的前端,那就不能太被动, 自己也研究了一下解决方案。
方案1:正则替换
如果我们使用的是axios
请求数据,Axios 提供了自定义处理原始后端返回数据的 API:transformResponse
, 可以这样处理:
js
axios({
method: method,
url: url,
data: data,
transformResponse: [function (data) {
// 将Long类型数据转换为字符串
const convertedJsonString = data.replace(/"(\w+)":(\d{15,})/g, '"$1":"$2"');
return JSON.parse(convertedJsonString);
}],
})
// 假设后端返回的JSON数据如下:
const responseData = {
id: 12345678901234567890, // 这是一个Long类型数据
name: "John Doe"
};
// 处理过的json数据
console.log(responseData.id); // 这将输出字符串:"12345678901234567890"
console.log(typeof responseData.id); // 这将输出 "string"
方案2:json序列化处理
我们可以借助json-bigint
这个第三方包来处理。
为什么我们不直接使用JSON.parse
可以看到,使用JSON.parse()
转换为JS对象,但是由于JS的Number的范围为,超出安全整数范围无法精确表示。
json-bigint
中的parse
方法会把超出 JS 安全整数范围的数字转为一个 BigNumber 类型的对象,对象数据是它内部的一个算法处理之后的,我们要做的就是在使用的时候转为字符串来使用。
通过启用storeAsString
选项,可以快速将BigNumber转为字符串,代码如下:
js
import JSONbig from "json-bigint";
axios({
method: method,
url: url,
data: data,
transformResponse: [function (data) {
+ const JSONbigToString = JSONbig({ storeAsString: true });
+ // 将Long类型数据转换为字符串
+ return JSONbigToString.parse(data);
}],
})
// 假设后端返回的JSON数据如下:
const responseData = {
id: 12345678901234567890, // 这是一个Long类型数据
name: "John Doe"
};
// 处理过的json数据
console.log(responseData.id); // 这将输出字符串:"12345678901234567890"
console.log(typeof responseData.id); // 这将输出 "string"
这个数据精度引发的血案到这里就告一段落,自己遇到了真的觉得有必要分享给小伙伴们,避免踩坑!当然最好还是让后端处理数据!
欢迎关注公众号《
前端界
》,文章会同步更新,也可快速加入前端交流群!