Vue項目中解決數(shù)字精度丟失問題
項目背景
標準一碼游后端接口返回數(shù)據(jù)好些都包含用戶敏感信息(姓名、手機號、身份證號),基于保護用戶隱私數(shù)據(jù)等原因,產(chǎn)品經(jīng)理提出需求,要求對涉及到這些數(shù)據(jù)的后端接口進行加密處理。為了方便快速開發(fā),同時盡量以最小的改動代價實現(xiàn)其目標,經(jīng)過和后端商量,前后端不采取一個個字段加解密的方式(這種方式修改起來涉及到較多的頁面的字段處理,前后端工作量都較大,不利于代碼維護),而是對涉及到的接口,整體進行加密傳輸(新增、編輯等操作),解密展示(詳情查詢、列表展示等操作)。
什么是數(shù)字精度丟失
我們知道,浮點類型的數(shù)據(jù),在計算機中是以二進制的方式存儲的,但是表示的數(shù)據(jù)也有個上限和下限。當超過限制 ,在計算機上顯示只能取最接近的限值。 數(shù)字解析精度丟失說的就是這個現(xiàn)象, 簡單講就是數(shù)字超過了位數(shù)不夠表示了。誰讓后端返回給前端這樣的大數(shù)字數(shù)據(jù)呢。
解決方案
數(shù)字解析精度丟失的常用前端解決方案,目前網(wǎng)上常用的有 bignumber.js、json-bigint、lossless-json等,其中json-bigint、lossless-json無疑是其中的佼佼者,特別是解析一堆json數(shù)據(jù)用于展示的時候,兩者使用起來真的不要太香,使用時只需使用兩者的parse、stringify方法即可,跟原生的JSON.parse、JSON.stringify沒有任何區(qū)別,唯一需要注意的地方就是使用前部分特殊參數(shù)的配置罷了,還有就是node環(huán)境不同,導致json-bigint、lossless-json無法正常安裝,或者安裝成功,運行報錯的情形。
小小抱怨一下
我就在最先使用lossless-json的時候,遇到了坑,當時的node環(huán)境不管怎么安裝到項目中,總之就是報錯,還是那種直接指向安裝包,讓人無從下手的感覺。害得我都下載了數(shù)次依賴,無數(shù)遍node環(huán)境,搞到npm都無法運行了,還有nvm都崩了。最后一次重新安裝的node環(huán)境終于可以了,我的那個坑爹,還是原來的node版本啊,當時只要一使用,就報錯,現(xiàn)在不鬧了,OK了,我找誰說理去啊(真不知道當時哪個環(huán)節(jié)影響到了)。
同時,下載的lossless-json版本,后臺項目(vite搭建,vue版本2.7.14)最新版本4.0.1,都ok了,但是挪移到小程序項目(webpack搭建,vue版本2.6.11)中的時候,又出幺蛾子了,只要一使用lossless-json,就報錯指向到 lossless-json 源碼中,又經(jīng)過了多次嘗試,最后降級到2.0.1版本,才終于不再報錯了。也不確定lossless-json的各個版本,是否和vue版本,或者搭建項目的框架(webpack、vite)有關,或者和node版本有關,具體我也沒時間慢慢研究,去查他的文檔,畢竟我們只是為了快速解決問題,完成任務,又不是他的代碼開發(fā)者,要去糾結這些,你說是不是呢。
回歸正題
考慮到項目中需要使用此功能的地方很多,為了方便管理維護,我定義了一個全局對象$jsonbig,然后將json-bigint(或者lossless-json)的 parse、stringify方法封裝到此對象中,最后在需要使用的地方直接調(diào)用即可。
this.$jsonbig.parse("JSON字符串");
this.$jsonbig.stringify("JSON對象");
下面我將具體講一講在實際項目中是如何使用它們的吧。
一、使用json-bigint
查看json-bigint文檔可參考: gitee yarn
如何使用
1、先在js中定義公共方法:
const JsonBigint = require('json-bigint')({
storeAsString: true
});
Object.defineProperty(Vue.prototype, '$jsonbig', {
value: JsonBigint
})
2、具體使用
// 在使用admate.js的后臺框架中使用,新增、編輯、查看詳情、分頁列表等關鍵代碼片段:
setup: () = >useAdmateAdapter({
urlPrefix: 'sot-admin-api/sys/company',
axiosConfig: {
r: row = >({
method: 'GET',
url: row.companyId
}),
d: {
method: 'POST'
},
getList: {
url: 'list'
},
u: {
headers: {
'Content-Type': 'application/json'
}
},
c: {
headers: {
'Content-Type': 'application/json'
}
}
},
list: {
filter: {
keyword: null,
areaCode: null,
contactName: '',
contactPhone: ''
},
dataAt: 'data.record'
},
form: {
data: {
__parentAreaCode: null,
address: null,
longitude: null,
latitude: null
}
}
},
{
afterGetList({ data }) {
this.$set(this.list, 'data', this.$jsonbig.parse(this.$decrypt(data.record)))
},
afterOpenForm({ data }) {
this.$set(this.form, 'data', this.$jsonbig.parse(this.$decrypt(data)))
}
}),
/**
* 獲取訂單詳情數(shù)據(jù)
* @return Void
*/
queryForDetail() {
this.$set(this.form__, "loading", true);
this.$POST(`sot - admin - api / hotel / self - order / queryForDetail`, {
id: this.form.data.orderId,
}).then(({ data }) = >{
this.$set(this.form__, "data", this.$jsonbig.parse(this.$decrypt(data)));
}).finally((_) = >{
this.$set(this.form__, "loading", false);
});
},
解釋一下,decrypt為項目中解密用到的公共方法,使用this.decrypt(data)獲取到的是json數(shù)據(jù)字符串,就是它可能丟失了數(shù)據(jù)精度。使用全局方法 this.$jsonbig.parse將得到我們需要JSON數(shù)據(jù)對象,經(jīng)過parse轉換后,json數(shù)據(jù)中可能丟失精度的字段值(BigNumber類型)都將統(tǒng)一轉為字符串類型(不論后端原本返回的是字符串還是數(shù)值類型)。
注意:
我們標準一碼游后臺是用vite搭建的vue項目,在引入json-bigint的時候,我們使用到了require,如下
require('json-bigint')({ storeAsString: true })
如果您以為像在webpack搭建出來的項目那樣,直接require引入就可以了的話,那你真的是想得簡單了。
如果你你在實際項目中跑了代碼,你會發(fā)現(xiàn),項目運行時會在console里報錯,項目也跑不起來了,
init.js:143 Uncaught ReferenceError: require is not defined at init.js:143

其實歸根結底是vite搭建的vue項目不支持commonJS語法,所以要使用require還得額外安裝插件, 推薦使用 vite-plugin-require-transform
// 下載
npm i vite-plugin-require-transform --save-dev
// 使用
// 在vite.config.js中引入,并找到plugins,在里面添加requireTransform 配置參數(shù)
import requireTransform from 'vite-plugin-require-transform';
plugins: [
// passing string type Regular expression
requireTransform({
fileRegex: /.js$|.vue$/
}),
],
說到這里,有人也許會說,我們不用require,改為import引入不就可以了,
require('json-bigint')({ storeAsString: true })
重點來了,這里引用 json-bigint 的時候,后面的參數(shù) storeAsString 這個參數(shù)很重要,不可省略(后面接著講),如果沒有參數(shù)storeAsString,我想大家都知道如何改為import
import JsonBigint from 'json-bigint'
這真的不用我啰嗦解釋啥,可是如果帶上參數(shù)呢,該怎么設置默認參數(shù)storeAsString,我嘗試了好幾種寫法,都沒成功,又看了下文檔和百度,都沒見到,或許再去翻一下json-bigint的源代碼,也許能找到答案,可是誰叫我太懶了呢,有誰知道的,可以直接留言告訴我,多謝了啊。
回到剛才的話題,剛剛提到的參數(shù) { storeAsString: true } ,為何說必不可少呢?
這是因為如果沒有設置這個參數(shù)為true,返回給我們的數(shù)據(jù),超精度的那個字段值,將是一個BigNumber類型,如下面截圖的companyId字段,

這種值,如要要在項目中展示或使用,是會出錯的,官方文檔告訴我們可以使用toString()或者String()單獨對這些字段進行格式化,可這次需求涉及到的頁面較多,每個改動到的后端接口返回的字段同樣很多,誰知道哪個字段精度丟失了,又在什么地方使用了(畢竟我們不是從頭一點點開發(fā)項目,遇到不對,立馬修改,而是在原來正常運營的項目中修改),因此 storeAsString這個參數(shù)就非常有用了,它使我們不必一個個的對BigNumber類型的字段進行處理,直接放心大膽的按原有的邏輯使用即可。
當然官方英文文檔不建議我們使用,說這是一種危險的行為,


看吧,兩個地方都這樣說,搞得我當時使用的時候心驚膽戰(zhàn)的,其實這破英文的意思:
指定BigInts是否應該以字符串的形式存儲在對象中,而不是默認的BigNumber。
請注意,這是一種危險的行為,因為它破壞了在不更改數(shù)據(jù)類型的情況下來回轉換的默認功能(因為這將把所有bigint轉換為be-and-stay字符串)。
看了中文解釋,現(xiàn)在我們再使用storeAsString設置為true,是不是瞬間放心不少,破英文單詞 dangerous 在這里嚇人呢 ,有沒有同感啊。
二、使用lossless-json
查看lossless-json文檔可參考: yarn
lossless-json使用和剛介紹的json-bigint類似,同樣是在js中定義公共方法,使用時完全一致,唯一不同的地方在于定義公共方法時為了將BigNumber類型字段自動轉化為字符串,lossless-json沒有類似storeAsString的參數(shù),而是要定義一個函數(shù)reviver,然后作為參數(shù)重寫入parse方法,具體如下設置:
import { parse, stringify } from 'lossless-json'
// 對number值進行轉換,如果不用這個方法,Number類型就變成LosslessNumber
const bigDataReviver = (key, value) = >{
if (value && value.isLosslessNumber) {
if (Number.isSafeInteger(parseInt(value.value))) {
return parseInt(value.value)
}
return value.value
} else {
return value
}
}
Object.defineProperty(Vue.prototype, '$jsonbig', {
value: {
parse: (keyword, reviver = bigDataReviver) = >{
return parse(keyword, reviver)
},
stringify
}
})
值得一提的是,lossless-json無法像下面這樣使用
import LosslessJson from 'lossless-json'
// 中間部分省略不顯示
Object.defineProperty(Vue.prototype, '$jsonbig', {
value: {
parse: (keyword, reviver = bigDataReviver) = >{
return LosslessJson.parse(keyword, reviver)
},
stringify: LosslessJson.stringify
}
})
如果你跑了項目,你可能會發(fā)現(xiàn),項目無法正常跑起來,并且在瀏覽器控制臺console欄,發(fā)現(xiàn)Uncaught SyntaxError報錯,如下圖

按照這英文提示,結合我們引用時候直接從對象中取parse, stringify方法 ,我們不難理解其含義,lossless-json并沒有向我們提供一個默認接口對象,讓我們通過一個對象直接調(diào)用parse, stringify方法,而是只暴露了parse, stringify。
就像我從json-bigint源碼中找到的,

紅框框選的這部分,在lossless-json中并沒有,所以才出現(xiàn)了上面的報錯。
關于在下載lossless-json引入使用報錯的情況,我文中開始就已經(jīng)提到,我就不再贅述,或許你遇都遇不到。
即使你真的遇到了,可以嘗試重裝,或者使用nvm切換node環(huán)境,再或者降級下載的版本,看能不能下載后使用吧,具體什么原因?qū)е碌?,我是沒有興趣慢慢嘗試和研究的,如果你有興趣,可以去查找資料或者問lossless-json的作者,我就不再這里繼續(xù)啰嗦了啊。
最后
以上就是Vue項目中解決數(shù)字精度丟失問題的詳細內(nèi)容,更多關于Vue數(shù)字精度丟失的資料請關注腳本之家其它相關文章!
相關文章
vue使用h函數(shù)封裝dialog組件(以命令的形式使用dialog組件)
文章介紹了如何將彈窗封裝成命令式形式,并解決了彈窗中的表單不能正常展示的問題,通過重新創(chuàng)建一個新的Vue應用實例并注冊所需的組件,解決了無法解析組件的警告,感興趣的朋友跟隨小編一起看看吧2026-01-01
Vue.js中用webpack合并打包多個組件并實現(xiàn)按需加載
對于現(xiàn)在前端插件的頻繁更新,我也是無力吐槽,但是既然入了前端的坑就得認嘛,所以多多少少要對組件化有點了解,下面這篇文章主要給大家介紹了在Vue.js中用webpack合并打包多個組件并實現(xiàn)按需加載的相關資料,需要的朋友可以參考下。2017-02-02

