Vue中雪花ID精度丢失问题:19位ID为何变成00结尾?

更新日期: 2025-12-01 阅读: 31 标签: 精度

在前后端联调过程中,"数据精度丢失"是个让人头疼的问题。前端页面可以正常添加数据,但进行删除或更新操作时却没有反应,服务端也没有报错。直到查看传输日志才发现问题:数据库里的ID是1951211313641689089,到了服务端却变成了1951211313641689000!

末尾几位数字被自动"抹平"了,这到底是怎么回事?


问题现象:能新增数据,但删除更新无效

问题场景
服务端使用雪花算法生成19位的大整数ID,前端通过vue调用接口获取数据列表。新增功能一切正常,但删除或更新操作时始终无法找到目标数据。

排查过程

  1. 先检查服务端:用api调试工具直接调用数据库操作接口,传入数据库中的真实ID,数据能正常删除和更新,说明服务端逻辑没问题

  2. 再检查前端:对比前端列表展示的ID和数据库原始ID,发现19位雪花ID的末尾几位被自动替换成了0

  3. 最终定位:问题出在前端对大整数ID的接收类型上,JavaScript的数字精度限制导致了ID"失真"


根本原因:JavaScript的数字精度限制

很多人不知道,JavaScript中的Number类型有个"最大安全整数"限制——Number.MAX_SAFE_INTEGER,这个值是9007199254740991(大约16位十进制数)。

而雪花算法生成的ID通常是18-19位,远远超过这个范围。JSON序列化时会默认将后端的Long类型转为前端的Number类型,超出安全范围的整数无法被精确表示,就会出现末尾数字被截断和替换的情况。

简单来说:19位的雪花ID对JavaScript的Number类型来说"太大了",它只能近似存储,自然会导致ID"变样"。服务端收到错误的ID后,找不到对应的数据,数据库操作也就失效了。


四种解决方案

方案一:用字符串存储ID(最直接)

将后端返回的ID以字符串形式接收和传输,避免转为Number类型,从根源上解决精度丢失问题:

// 前端接收数据时,确保ID以字符串形式存储
const list = res.data.map(item => ({
  ...item,
  id: item.id.toString() // 强制转为字符串
}));

// 调用删除接口时,直接传字符串ID
const deleteData = async (id) => {
  await axios.post('/api/delete', { id }); // id为字符串类型,不会有精度丢失
};

方案二:使用BigInt类型(ES2020+特性)

BigInt专门用于表示大整数,支持任意精度:

// 方式1:直接声明(在数字末尾加n)
const bigId = 1951211313641689089n;

// 方式2:从字符串转换(推荐,避免直接写大数字)
const bigId = BigInt("1951211313641689089");

// 调用接口时注意:需要转为字符串传输
await axios.post('/api/delete', { id: bigId.toString() });

方案三:使用bignumber.js库

如果项目需要频繁处理大数字,可以使用成熟的第三方库:

# 安装依赖
npm install bignumber.js
import BigNumber from 'bignumber.js';

// 初始化大数字ID
const bigId = new BigNumber("1951211313641689089");

// 传输时转为字符串
const deleteData = async (id) => {
  await axios.post('/api/delete', { id: id.toString() });
};

方案四:Axios全局配置(推荐)

如果项目使用Axios请求接口,可以通过json-bigint库重写响应解析逻辑:

# 安装依赖
npm install json-bigint
import axios from 'axios';
import JSONbig from 'json-bigint';

// 创建自定义Axios实例
const api = axios.create({
  transformResponse: [data => {
    try {
      // 大数字转为字符串存储
      return JSONbig({ storeAsString: true }).parse(data);
    } catch (e) {
      // 解析失败时回退到普通JSON解析
      return JSON.parse(data);
    }
  }]
});

// 后续所有请求使用这个api实例,ID会自动转为字符串
export const deleteData = async (id) => {
  return await api.post('/api/delete', { id });
};

export const getDataList = async () => {
  return await api.get('/api/list'); // 返回的ID已经是字符串类型
};


Vue组件中的实际应用

在Vue组件中处理大数字ID时,可以这样写:

<template>
  <div>
    <div v-for="item in list" :key="item.id">
      {{ item.name }}
      <button @click="deleteItem(item.id)">删除</button>
    </div>
  </div>
</template>

<script>
import { getDataList, deleteData } from '@/api';

export default {
  data() {
    return {
      list: []
    }
  },
  async mounted() {
    await this.loadData();
  },
  methods: {
    async loadData() {
      const res = await getDataList();
      // 确保ID是字符串类型
      this.list = res.data.map(item => ({
        ...item,
        id: String(item.id)
      }));
    },
    async deleteItem(id) {
      // id已经是字符串,直接使用
      await deleteData(id);
      await this.loadData(); // 重新加载数据
    }
  }
}
</script>


避坑经验总结

  1. 涉及雪花算法、UUID等长ID时,前端优先用字符串类型接收和传输,不要依赖Number类型

  2. 前后端约定数据类型时,明确大整数的传输格式(建议统一用字符串),避免默认序列化导致的隐式转换

  3. 使用Axios等请求库时,可以提前配置全局的大数字处理规则,减少重复代码

  4. 遇到"数据存在但操作失效"的情况,先排查ID、手机号等长数字的传输精度,大概率是类型转换出了问题

  5. 在Vue的模板中,使用字符串形式的ID作为key,确保Vue能正确识别每个列表项


后端配合方案

虽然本文主要讨论前端解决方案,但后端也可以配合:

// Spring Boot 示例:将Long类型ID序列化为字符串
public class User {
    @JsonSerialize(using = ToStringSerializer.class)
    private Long id;
    
    // 其他字段...
}

这样前端接收到的ID直接就是字符串格式,避免了精度丢失问题。


总结

这类问题不是Vue框架或服务端的责任,而是JavaScript底层类型限制导致的。只要提前做好类型规划,使用正确的处理方案,就能轻松避免"ID变样"的尴尬情况。

对于新项目,建议从一开始就使用字符串类型来处理大数字ID;对于现有项目,可以使用Axios全局配置方案来快速修复问题。记住,预防总比修复来得容易。

本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/13238

JavaScript浮点数精度

0.1 + 0.2 是否等于 0.3 作为一道经典的面试题,已经广外熟知,说起原因,大家能回答出这是浮点数精度问题导致,也能辩证的看待这并非是 ECMAScript 这门语言的问题,今天就是具体看一下背后的原因。

js浮点数运算时出现精度问题以及保留指定精度位

因为toFixed可能会出现bug,比如value为: 310.275,保留2位小数,为310.27;或者是value为: 139.605 ,保留2位小数,为: 139.60

如何处理 JavaScript 中的浮点数精度?

ES6 在Number对象上面,新增一个极小的常量Number.EPSILON。它表示 1 与大于 1 的最小浮点数之间的差。Number.EPSILON实际上是 JavaScript 能够表示的最小精度。误差如果小于这个值,就可以认为已经没有意义了

Js大数运算精度问题,如何实现两个大数相加?

JS 在存放整数的时候是有一个安全范围的,一旦数字超过这个范围便会损失精度。我们不能拿精度损失的数字进行运行,因为运算结果一样是会损失精度的。所以,我们要用字符串来表示数据!(不会丢失精度)

都知道0.1+0.2 = 0.30000000000000004,那要怎么让它等于0.3

小学数学老师教过我们,0.1 + 0.2 = 0.3,但是为什么在我们在浏览器的控制台中输出却是0.30000000000000004?除了加法有这个奇怪的现象,带小数点的减法和乘除计算也会得出意料之外的结果

Js精度丢失原因以及解决方案

由于计算机的底层是由二进制实现的,有些运算的数字无法全部显示出来。就像一些无理数不能完全显示出来一样,如圆周率 3.1415926...,0.3333... 等。JavaScript遵循IEEE754规范

JS中浮点数精度问题_小数四舍五入和浮点数的研究

最近在做项目的时候,涉及到商品价格的计算,经常会出现计算出现精度问题。刚开始草草了事,直接用toFixed就解决了问题,并没有好好的思考一下这个问题

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!