直接用 fetch 发送含 BigInt 的数据会失败,因 JSON.stringify 遇 BigInt 抛 TypeError;需用 replacer 函数将其统一转为字符串,并配合字段命名约定(如 _big 后缀)便于后端还原,推荐封装为 safeJsonStringify 工具函数。

直接用 fetch 发送含 BigInt 的数据会失败,因为 JSON.stringify 遇到 123n 会立刻抛出 TypeError: Do not know how to serialize a BigInt——不是静默丢数,而是请求根本发不出。解决核心就一条:**不让 BigInt 进入默认序列化流程,提前转成安全格式**。
用 replacer 函数统一转字符串(最推荐)
这是兼容性最好、侵入最小、语义最清晰的方式。所有 BigInt 在序列化前自动变成字符串,精度零丢失,后端也容易识别和处理。
- 在调用
JSON.stringify时传入第二个参数replacer,判断类型并转换:const body = JSON.stringify(data, (key, value) => typeof value === 'bigint' ? value.toString() : value); - 搭配
fetch使用时,确保Content-Type是application/json,且 body 是上述生成的字符串 - 示例:
const payload = { id: 9007199254740991n, total: 1000000000000000000000000n };<br>fetch('/api/order', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload, (k, v) => typeof v === 'bigint' ? v.toString() : v) });
给关键字段加命名约定,便于后端还原
光转字符串还不够——后端需要知道哪个字符串原本是 BigInt,否则可能误当普通字符串或 Number 处理。建议对字段名做轻量级约定:
- 比如 ID、金额、计数类字段统一用
_big后缀:{ user_id_big: "12345678901234567890", balance_big: "999999999999999999999" } - 这样后端解析时可精准匹配:
if (key.endsWith('_big') && typeof value === 'string') return BigInt(value); - 避免全局搜索字符串或依赖注释,降低歧义风险
封装成可复用的请求工具函数
每次手动写 replacer 容易遗漏。把序列化逻辑收口,让业务代码保持干净:
- 定义一个
safeJsonStringify工具函数,内部固定处理bigint、Date等常见不可序列化类型 - 再封装
apiPost等方法,自动使用该序列化器:export async function apiPost(url, data) {<br> return fetch(url, {<br> method: 'POST',<br> headers: { 'Content-Type': 'application/json' },<br> body: safeJsonStringify(data)<br> });<br>} - 后续所有接口调用只需
apiPost('/user', { id: 1n }),无需关心底层转换
不建议用 BigInt.prototype.toJSON
虽然加一行 BigInt.prototype.toJSON = function() { return this.toString(); } 能让 JSON.stringify “静默通过”,但隐患明显:
- TypeScript 类型系统不认,编译报错,需额外声明或禁用检查
- 污染全局原型,在微前端或多库共存项目中可能引发冲突
- 只解决序列化,反序列化仍需额外逻辑,无法形成闭环
- 违反“显式优于隐式”原则,排查问题时更难定位源头

















