后端数据返回number类型的id,前端显示精度丢失了,怎么办?
# 起因
在工作中遇到这样一个问题,后端传来JSON
字符串,前端放到页面的code编辑器中格式化展示。
如果直接赋值展示,code编辑器中不会进行格式化,只会是一长串的字符串缩在一起,需要前端将其解析后让数据格式化展示。
代码方面,前端先将接口返回的JSON
字符串用JSON.parse
转为对象类型,然后再用JSON.stringify
,第三个参数传\t
缩进,转为JSON
类型,再给code赋值,就能在code编辑器中格式化展现数据,如图(隐私信息已打码)

# 问题
但问题在于,如图中的cardNo
、documentId
,后端可能传来的是number
类型,前端在JSON.parse
后,数据精度就丢失了
如'{"id":3021241541248712345}'
,JSON.parse后就会变成{id: 3021241541248712000}
首先想到的解决办法,就是叫后端更改数据类型,id
之类的怎么看都应该是定义为string
类型啊
然而,后端说,这个接口是第三方接口,他们也改不了,所以只能前端解决。。。
第三方接口返回的数据多种多样,质量很差,主要有这两个问题:
不一定是JSON类型,导致
JSON.parse
解析报错;传来超过js精度的大数类型,导致
JSON.parse
后数据精度丢失,后几位为000
# 解决方案
想到的解决方案为以下两个
# 1.通过第三方插件 json-bigint (opens new window)
部分代码如下:
import JSONbig from 'json-bigint'
const parseRes = JSONbig.parse(jsonStr)
const stringifyRes = JSONbig.stringify(parseRes)
this.codeData = stringifyRes
优点:
1.使用起来简单,方便快捷,为较成熟的第三方库,有人进行维护;
2.两次转化后并没有改变数据的数据类型,大数仍然是number
类型。
缺点:
1.使用第三方库,需下载引入依赖,需要考虑你的leader是否允许使用这个第三方库;
2.更重要的是,在这个项目中,是为了在code编辑器中反显正确数值,但是这里的处理尽管parseRes
里的大数值被转为了BigInt
类型,stringifyRes
里也是正确的数值,但给编辑器赋值后,大数精度还是丢失了。就像是在浏览器控制台,随便输入一个大数,一回车,返回的就是精度丢失的数值,所以这个方法在该项目不可行
别的场景下这个库能用,可以做首选解决方案,但是不行的话就只能降级为方法2了
# 2.用正则将大数类型转为字符串
在JSON.parse
前,通过正则,将大数类型转为字符串,但需要考虑很多场景:
js能处理的最大整数为
Math.pow(2, 53)
,十进制即9007199254740992
(16位),所以超过15位的数就要做转化后端返回的数据,可能是仅为大数的;也可能是超过15数,但其类型已经是字符串的,即前面就有单双引号;也可能是一串网址字符串,只是中间一串超过15位的数字的。
所以正则就需要考虑这些来写,可能还有很多其他情况,所以就要根据实际业务不断完善正则了
最后处理完整代码如下:
// 若是返回的为仅数字的,就不需要转了
if (/^\d+$/.test(jsonStr)) {
this.codeData = jsonStr
} else {
// 对json字符串中超过15位的大数进行转为字符串
const conversionJsonStr = jsonStr.replace(/([^"'\\u\/0-9])([0-9]{15,})/g, '$1"$2"')
try {
// 转为js对象
const jsObj = JSON.parse(conversionJsonStr)
// 再转为格式化字符串
this.codeData = JSON.stringify(jsObj, null, '\t')
} catch (error) {
// 接口数据非JSON的情况,或是我们转化的字符串,导致JSON.parse报错的情况
console.log(error)
// 对于这些JSON.parse无法处理报错的数据,就只能先直接原样输出
// 数据问题就罢了,转化的问题就只能后续再不断补充完善了
this.codeData = jsonStr
}
}
页面的code编辑器使用的是vue-codemirror这个插件