前后端传输 Long 类型数据时(时间戳,雪花算法ID),精度丢失的根本原因
前后端传输 Long 类型数据时,精度丢失的根本原因是 JavaScript 的 Number 类型无法精确表示超过 53 位(64 位双精度浮点数)的整数,而 Java 的 Long 类型是 64 位整数。当后端返回的 Long 值超过 2^53-1
(即 9007199254740991
)时,前端解析会出现精度错误。
解决方案
方案一:后端将 Long 转为字符串(推荐)
后端修改(Spring Boot 示例):
-
局部方案:在字段上添加
@JsonFormat
注解public class UserDTO {@JsonFormat(shape = JsonFormat.Shape.STRING)private Long id; // 自动序列化为字符串 }
-
全局方案:配置 Jackson 序列化规则
@Configuration public class JacksonConfig {@Beanpublic Jackson2ObjectMapperBuilder jacksonBuilder() {Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();// 所有 Long 类型转为字符串builder.serializerByType(Long.class, ToStringSerializer.instance);return builder;} }
前端处理:
直接使用字符串类型的 ID,避免用 Number
类型解析:
javascript
// 正确:后端返回 { "id": "1234567890123456789" }
fetch('/api/user').then(res => res.json()).then(data => {const userId = data.id; // 直接作为字符串使用console.log(userId); // "1234567890123456789"});
方案二:前端特殊处理大整数
使用 JSON.parse 自定义解析(需确保后端不转字符串):
// 使用 json-bigint 库处理大整数
import JSONbig from 'json-bigint';const response = await fetch('/api/data');
const text = await response.text();
const data = JSONbig.parse(text); // 自动将大数字转为 BigInt 类型// 使用示例
console.log(data.id.toString()); // 转为字符串操作
方案三:后端使用 String 类型代替 Long
从根本上避免问题:
public class UserDTO {private String id; // 直接定义为字符串类型
}
关键原因说明
类型 | 范围 | 精度限制 |
---|---|---|
Java Long | -9223372036854775808 ~ 9223372036854775807 | 64 位整数,无精度损失 |
JavaScript Number | ±9007199254740991 以内安全 | 超过 53 位丢失精度 |
最佳实践
-
优先推荐方案一:后端统一将 Long 序列化为字符串,前端按字符串处理。
-
若前端需数值运算:将字符串转为
BigInt
(注意浏览器兼容性):const bigIntId = BigInt("1234567890123456789"); // 字符串转BigInt
-
避免使用
Number()
或parseInt()
转换大数字符串,否则仍会丢失精度。
通过统一数据类型定义(字符串传输),可彻底解决此问题,同时保持数据精确性。