最近在项目里处理企业信息接口,遇到一个很隐蔽的问题:
后端返回的企业 ID 明明是一个值,前端拿到后却"变了"。这是前端把大整数精度丢了。
这篇就聊三件事:为什么会丢、什么时候丢、怎么彻底规避。
先看现象:两个数相等?
js
9007199254740992 === 9007199254740993 // true
看到这个结果,很多人第一反应是"JS 出 bug 了"。
其实没有,这是 JavaScript 数值模型决定的。
原理:为什么会精度丢失?
JavaScript 的 number 底层是 IEEE 754 双精度浮点数 。
它并不是"任意精度整数",只有 53 位有效精度(含隐藏位)。
所以 JS 能安全表示的最大整数是:
js
Number.MAX_SAFE_INTEGER // 9007199254740991(2^53 - 1)
一旦超过这个范围,就会出现:
- 相邻整数无法都精确表示(中间会跳号)
- 不同整数可能映射到同一个可表示值
这就是"大整数看起来被改了"的根因。
关键点:精度丢失通常发生在 JSON 解析阶段
很多同学以为是运算时才丢精度,其实在前端接收响应时就可能已经丢了。
比如后端返回:
json
{ "id": 9876543210123456789 }
如果你直接走原生 JSON.parse,这个值会被转成 JS number。
而超出安全范围后,精度损失通常是不可逆的。
我在项目里的方案:请求层统一接入 json-bigint
我在 axios 的 transformResponse 里用了 json-bigint,并配置:
storeAsString: true
意思是:大整数不要转 number,直接按字符串保存,确保"值不变形"。
实际代码(简化版)
ts
import axios from 'axios'
import JSONbig from 'json-bigint'
const JSONbigParser = JSONbig({ storeAsString: true })
const service = axios.create({
transformResponse: [
function (data) {
if (!data) return data
try {
// 用 JSONbig 解析,超大数字按字符串保留
return JSONbigParser.parse(data)
} catch (err) {
console.warn('JSON 解析失败,返回原始数据', err)
return data
}
},
],
})
用了以后会发生什么变化?
后端:
json
{ "companyId": 9876543210123456789 }
前端拿到:
json
{ "companyId": "9876543210123456789" }
精度保住了,值可回传、可比对、可展示。