JavaScript大数ID精度丢失问题的解决方案

 更新时间:2025年09月11日 10:39:50   作者:当归1024  
在前后端分离的Web应用开发中,经常会遇到一个令人头疼的问题:JavaScript数字精度丢失,当后端使用Long类型生成的大数ID传递到前端时,会出现精度丢失,所以本文给大家介绍了JavaScript大数ID精度丢失问题解决方案,需要的朋友可以参考下

问题背景

在前后端分离的Web应用开发中,经常会遇到一个令人头疼的问题:JavaScript数字精度丢失。当后端使用Long类型生成的大数ID传递到前端时,会出现精度丢失,导致前端获取的ID与后端实际存储的ID不一致。

问题现象

在我们的若依(RuoYi)邮件服务器管理系统中,出现了以下问题:

  • 数据库中的ID: 1965951881494065154
  • Vue前端接收到的ID: 1965951881494065200
  • 结果: 修改操作失败,因为前端发送的ID与数据库中的实际ID不匹配

问题根源分析

JavaScript数字精度限制

JavaScript中的数字都是以64位浮点数存储的,其中:

  • 1位符号位
  • 11位指数位
  • 52位尾数位

这意味着JavaScript能够安全表示的最大整数是 2^53 - 1 = 9007199254740991,即 Number.MAX_SAFE_INTEGER

雪花算法ID的特点

现代分布式系统中常用的雪花算法(Snowflake)生成的ID通常是19位的长整型数字,如:

1965951881494065154 (19位)

这个数字远远超过了JavaScript的安全整数范围,因此在JSON序列化传输到前端时会发生精度丢失。

解决方案

核心思路

将Long类型的ID在JSON序列化时转换为字符串,避免JavaScript的数字精度问题。

使用Jackson的ToStringSerializer

在Java实体类中,对ID字段添加 @JsonSerialize(using = ToStringSerializer.class) 注解。

实施步骤

1. 添加必要的导入

在实体类中添加Jackson相关的导入:

import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;

2. 在ID字段上添加注解

/** 服务器ID */
@JsonSerialize(using = ToStringSerializer.class)
private Long id;

3. 完整的实体类示例

package com.ruoyi.email.domain;

import java.io.Serializable;
import java.util.Date;
import javax.validation.constraints.*;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import lombok.Data;
import lombok.EqualsAndHashCode;

@Data
@EqualsAndHashCode
@TableName("tbl_email_server")
public class EmailServer implements Serializable
{
    private static final long serialVersionUID = 1L;

    /** 服务器ID */
    @JsonSerialize(using = ToStringSerializer.class)
    private Long id;
    
    // ... 其他字段
}

解决方案的工作原理

序列化过程

  1. 后端序列化: Long类型的ID 1965951881494065154 被序列化为字符串 "1965951881494065154"
  2. 前端接收: JavaScript接收到字符串格式的ID,不会有精度丢失
  3. 前端发送: 前端将字符串ID发送回后端
  4. 后端反序列化: Spring Boot自动将字符串转换回Long类型

JSON数据格式对比

修改前:

{
    "id": 1965951881494065200,  // 精度丢失!
    "serverCode": "EMAIL_001",
    "name": "主邮件服务器"
}

修改后:

{
    "id": "1965951881494065154", // 字符串格式,精度保持!
    "serverCode": "EMAIL_001", 
    "name": "主邮件服务器"
}

前端处理注意事项

HTML模板中的处理

由于ID现在是字符串格式,在前端模板中可以正常使用:

<template>
  <el-table :data="serverList">
    <el-table-column prop="id" label="ID" />
    <el-table-column label="操作">
      <template #default="{ row }">
        <!-- 字符串ID可以直接使用 -->
        <el-button @click="editServer(row.id)">编辑</el-button>
      </template>
    </el-table-column>
  </el-table>
</template>

JavaScript中的处理

// 接收数据
const serverData = {
    id: "1965951881494065154", // 字符串格式
    serverCode: "EMAIL_001",
    name: "主邮件服务器"
};

// 发送请求
updateEmailServer({
    id: serverData.id, // 直接使用字符串ID
    serverCode: "EMAIL_002"
});

验证结果

修改完成后,验证效果:

  1. 数据库ID: 1965951881494065154
  2. 前端接收ID: "1965951881494065154" (字符串格式)
  3. 前端发送ID: "1965951881494065154"
  4. 后端接收ID: 1965951881494065154 (自动转换为Long)

结果: ID数据在前后端传输过程中保持完全一致,修改操作成功!

适用场景

这个解决方案适用于以下情况:

  1. 雪花算法生成的ID: 通常是18-19位的长整型
  2. 时间戳ID: 毫秒级时间戳生成的长整型ID
  3. 其他超过JavaScript安全范围的Long类型字段: 如订单号、交易流水号等

性能影响

  • 序列化性能: 几乎无影响,字符串序列化很高效
  • 网络传输: 略微增加传输字节数(引号)
  • 前端处理: 字符串比较操作性能略优于数字比较

其他解决方案对比

方案一:前端使用BigInt

// 需要特殊处理
const id = BigInt("1965951881494065154");

缺点: 需要修改大量前端代码,兼容性问题

方案二:自定义JSON配置

@Configuration
public class JacksonConfig {
    @Bean
    public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
        return new Jackson2ObjectMapperBuilder()
                .simpleDateFormat("yyyy-MM-dd HH:mm:ss")
                .serializerByType(Long.class, ToStringSerializer.instance);
    }
}

缺点: 影响全局所有Long类型字段

方案三:使用@JsonSerialize注解

@JsonSerialize(using = ToStringSerializer.class)
private Long id;

优点:

  • 精确控制,只影响特定字段
  • 无需修改前端代码
  • 性能影响最小
  • 代码清晰易维护

总结

通过在Java实体类的ID字段上添加 @JsonSerialize(using = ToStringSerializer.class) 注解,我们成功解决了JavaScript大数精度丢失的问题。这个方案具有以下优势:

  1. 简单易实施: 只需添加一个注解
  2. 影响范围小: 只影响特定字段,不会波及其他功能
  3. 兼容性好: 前端代码无需修改
  4. 性能优秀: 几乎无性能损耗

这个问题在现代Web开发中非常常见,特别是在使用分布式ID生成算法的系统中。掌握这个解决方案能够帮助开发者快速解决类似的精度丢失问题,提高系统的稳定性和数据准确性。

以上就是JavaScript大数ID精度丢失问题的解决方案的详细内容,更多关于JavaScript大数ID精度丢失的资料请关注脚本之家其它相关文章!

相关文章

  • 微信小程序之裁剪图片成圆形的实现代码

    微信小程序之裁剪图片成圆形的实现代码

    最近在开发小程序,产品经理提了一个需求,要求微信小程序换头像,用户剪裁图片必须是圆形。这篇文章主要介绍了微信小程序之裁剪图片成圆形 ,需要的朋友可以参考下
    2018-10-10
  • JS求Number类型数组中最大元素方法

    JS求Number类型数组中最大元素方法

    这篇文章主要介绍了如何用JS求Number类型数组中最大元素
    2018-04-04
  • javascript实现仿银行密码输入框效果的代码

    javascript实现仿银行密码输入框效果的代码

    这篇文章通过实例代码给大家介绍了javascript实现仿银行密码输入框效果,代码简单易懂,非常不错,具有一定的参考借鉴价值,需要的朋友参考下吧
    2007-12-12
  • JavaScript中关于递归与回溯的实例详解

    JavaScript中关于递归与回溯的实例详解

    这篇文章主要将为大家介绍一下JavaScript中递归与回溯的原理及使用,文中通过一些例题进行了详细介绍,感兴趣的小伙伴可以跟随小编一起学习一下
    2022-07-07
  • javascript常见操作汇总

    javascript常见操作汇总

    这篇文章主要介绍了javascript常见操作,有针对字符串、时间、表单等的操作,以及验证、收藏、随机数等其他常见技巧,需要的朋友可以参考下
    2014-09-09
  • JS Canvas接口和动画效果大全

    JS Canvas接口和动画效果大全

    这篇文章主要介绍了JS Canvas接口和动画效果大全,对Canvas感兴趣的同学,可以参考下
    2021-04-04
  • indexOf 和 lastIndexOf 使用示例介绍

    indexOf 和 lastIndexOf 使用示例介绍

    indexOf是在一个字符串中寻找一个字的位置,lastIndexOf 也是找字 , 它们俩的区别是前者从字符串头开始找,后者是从字符串末端开始找
    2014-09-09
  • 让mocha支持ES6模块的方法实现

    让mocha支持ES6模块的方法实现

    这篇文章主要介绍了让mocha支持ES6模块的方法实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-01-01
  • JS实现点击表头表格自动排序(含数字、字符串、日期)

    JS实现点击表头表格自动排序(含数字、字符串、日期)

    这篇文章主要介绍了利用JS如何实现点击表头后表格自动排序,其中包含数字排序、字符串排序以及日期格式的排序,文中给出了完整的示例代码,相信对大家具有一定的参考价值,感兴趣的朋友们一起来看看吧。
    2017-01-01
  • javascript 可控式透明特效实现代码

    javascript 可控式透明特效实现代码

    透明特效是script.aculo.us提到的特效中最简单的特效之一。既然是特效,必须涉及时间与空间的概念。时间我们可以用setTimeout与setInterval,个人比较喜欢setTimeout,虽然它每次调用都重复注册,但可控性比较好。
    2010-01-01

最新评论