Vue項(xiàng)目中解決數(shù)字精度丟失問題
項(xiàng)目背景
標(biāo)準(zhǔn)一碼游后端接口返回?cái)?shù)據(jù)好些都包含用戶敏感信息(姓名、手機(jī)號、身份證號),基于保護(hù)用戶隱私數(shù)據(jù)等原因,產(chǎn)品經(jīng)理提出需求,要求對涉及到這些數(shù)據(jù)的后端接口進(jìn)行加密處理。為了方便快速開發(fā),同時(shí)盡量以最小的改動代價(jià)實(shí)現(xiàn)其目標(biāo),經(jīng)過和后端商量,前后端不采取一個(gè)個(gè)字段加解密的方式(這種方式修改起來涉及到較多的頁面的字段處理,前后端工作量都較大,不利于代碼維護(hù)),而是對涉及到的接口,整體進(jìn)行加密傳輸(新增、編輯等操作),解密展示(詳情查詢、列表展示等操作)。
什么是數(shù)字精度丟失
我們知道,浮點(diǎn)類型的數(shù)據(jù),在計(jì)算機(jī)中是以二進(jìn)制的方式存儲的,但是表示的數(shù)據(jù)也有個(gè)上限和下限。當(dāng)超過限制 ,在計(jì)算機(jī)上顯示只能取最接近的限值。 數(shù)字解析精度丟失說的就是這個(gè)現(xiàn)象, 簡單講就是數(shù)字超過了位數(shù)不夠表示了。誰讓后端返回給前端這樣的大數(shù)字?jǐn)?shù)據(jù)呢。
解決方案
數(shù)字解析精度丟失的常用前端解決方案,目前網(wǎng)上常用的有 bignumber.js、json-bigint、lossless-json等,其中json-bigint、lossless-json無疑是其中的佼佼者,特別是解析一堆json數(shù)據(jù)用于展示的時(shí)候,兩者使用起來真的不要太香,使用時(shí)只需使用兩者的parse、stringify方法即可,跟原生的JSON.parse、JSON.stringify沒有任何區(qū)別,唯一需要注意的地方就是使用前部分特殊參數(shù)的配置罷了,還有就是node環(huán)境不同,導(dǎo)致json-bigint、lossless-json無法正常安裝,或者安裝成功,運(yùn)行報(bào)錯(cuò)的情形。
小小抱怨一下
我就在最先使用lossless-json的時(shí)候,遇到了坑,當(dāng)時(shí)的node環(huán)境不管怎么安裝到項(xiàng)目中,總之就是報(bào)錯(cuò),還是那種直接指向安裝包,讓人無從下手的感覺。害得我都下載了數(shù)次依賴,無數(shù)遍node環(huán)境,搞到npm都無法運(yùn)行了,還有nvm都崩了。最后一次重新安裝的node環(huán)境終于可以了,我的那個(gè)坑爹,還是原來的node版本啊,當(dāng)時(shí)只要一使用,就報(bào)錯(cuò),現(xiàn)在不鬧了,OK了,我找誰說理去?。ㄕ娌恢喇?dāng)時(shí)哪個(gè)環(huán)節(jié)影響到了)。
同時(shí),下載的lossless-json版本,后臺項(xiàng)目(vite搭建,vue版本2.7.14)最新版本4.0.1,都o(jì)k了,但是挪移到小程序項(xiàng)目(webpack搭建,vue版本2.6.11)中的時(shí)候,又出幺蛾子了,只要一使用lossless-json,就報(bào)錯(cuò)指向到 lossless-json 源碼中,又經(jīng)過了多次嘗試,最后降級到2.0.1版本,才終于不再報(bào)錯(cuò)了。也不確定lossless-json的各個(gè)版本,是否和vue版本,或者搭建項(xiàng)目的框架(webpack、vite)有關(guān),或者和node版本有關(guān),具體我也沒時(shí)間慢慢研究,去查他的文檔,畢竟我們只是為了快速解決問題,完成任務(wù),又不是他的代碼開發(fā)者,要去糾結(jié)這些,你說是不是呢。
回歸正題
考慮到項(xiàng)目中需要使用此功能的地方很多,為了方便管理維護(hù),我定義了一個(gè)全局對象$jsonbig,然后將json-bigint(或者lossless-json)的 parse、stringify方法封裝到此對象中,最后在需要使用的地方直接調(diào)用即可。
this.$jsonbig.parse("JSON字符串"); this.$jsonbig.stringify("JSON對象");
下面我將具體講一講在實(shí)際項(xiàng)目中是如何使用它們的吧。
一、使用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的后臺框架中使用,新增、編輯、查看詳情、分頁列表等關(guān)鍵代碼片段: 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為項(xiàng)目中解密用到的公共方法,使用this.decrypt(data)獲取到的是json數(shù)據(jù)字符串,就是它可能丟失了數(shù)據(jù)精度。使用全局方法 this.$jsonbig.parse將得到我們需要JSON數(shù)據(jù)對象,經(jīng)過parse轉(zhuǎn)換后,json數(shù)據(jù)中可能丟失精度的字段值(BigNumber類型)都將統(tǒng)一轉(zhuǎn)為字符串類型(不論后端原本返回的是字符串還是數(shù)值類型)。
注意:
我們標(biāo)準(zhǔn)一碼游后臺是用vite搭建的vue項(xiàng)目,在引入json-bigint的時(shí)候,我們使用到了require,如下
require('json-bigint')({ storeAsString: true })
如果您以為像在webpack搭建出來的項(xiàng)目那樣,直接require引入就可以了的話,那你真的是想得簡單了。
如果你你在實(shí)際項(xiàng)目中跑了代碼,你會發(fā)現(xiàn),項(xiàng)目運(yùn)行時(shí)會在console里報(bào)錯(cuò),項(xiàng)目也跑不起來了,
init.js:143 Uncaught ReferenceError: require is not defined at init.js:143
其實(shí)歸根結(jié)底是vite搭建的vue項(xiàng)目不支持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 })
重點(diǎn)來了,這里引用 json-bigint 的時(shí)候,后面的參數(shù) storeAsString 這個(gè)參數(shù)很重要,不可省略(后面接著講),如果沒有參數(shù)storeAsString,我想大家都知道如何改為import
import JsonBigint from 'json-bigint'
這真的不用我啰嗦解釋啥,可是如果帶上參數(shù)呢,該怎么設(shè)置默認(rèn)參數(shù)storeAsString,我嘗試了好幾種寫法,都沒成功,又看了下文檔和百度,都沒見到,或許再去翻一下json-bigint的源代碼,也許能找到答案,可是誰叫我太懶了呢,有誰知道的,可以直接留言告訴我,多謝了啊。
回到剛才的話題,剛剛提到的參數(shù) { storeAsString: true } ,為何說必不可少呢?
這是因?yàn)槿绻麤]有設(shè)置這個(gè)參數(shù)為true,返回給我們的數(shù)據(jù),超精度的那個(gè)字段值,將是一個(gè)BigNumber類型,如下面截圖的companyId字段,
這種值,如要要在項(xiàng)目中展示或使用,是會出錯(cuò)的,官方文檔告訴我們可以使用toString()或者String()單獨(dú)對這些字段進(jìn)行格式化,可這次需求涉及到的頁面較多,每個(gè)改動到的后端接口返回的字段同樣很多,誰知道哪個(gè)字段精度丟失了,又在什么地方使用了(畢竟我們不是從頭一點(diǎn)點(diǎn)開發(fā)項(xiàng)目,遇到不對,立馬修改,而是在原來正常運(yùn)營的項(xiàng)目中修改),因此 storeAsString這個(gè)參數(shù)就非常有用了,它使我們不必一個(gè)個(gè)的對BigNumber類型的字段進(jìn)行處理,直接放心大膽的按原有的邏輯使用即可。
當(dāng)然官方英文文檔不建議我們使用,說這是一種危險(xiǎn)的行為,
看吧,兩個(gè)地方都這樣說,搞得我當(dāng)時(shí)使用的時(shí)候心驚膽戰(zhàn)的,其實(shí)這破英文的意思:
指定BigInts是否應(yīng)該以字符串的形式存儲在對象中,而不是默認(rèn)的BigNumber。
請注意,這是一種危險(xiǎn)的行為,因?yàn)樗茐牧嗽诓桓臄?shù)據(jù)類型的情況下來回轉(zhuǎn)換的默認(rèn)功能(因?yàn)檫@將把所有bigint轉(zhuǎn)換為be-and-stay字符串)。
看了中文解釋,現(xiàn)在我們再使用storeAsString設(shè)置為true,是不是瞬間放心不少,破英文單詞 dangerous 在這里嚇人呢 ,有沒有同感啊。
二、使用lossless-json
查看lossless-json文檔可參考: yarn
lossless-json使用和剛介紹的json-bigint類似,同樣是在js中定義公共方法,使用時(shí)完全一致,唯一不同的地方在于定義公共方法時(shí)為了將BigNumber類型字段自動轉(zhuǎn)化為字符串,lossless-json沒有類似storeAsString的參數(shù),而是要定義一個(gè)函數(shù)reviver,然后作為參數(shù)重寫入parse方法,具體如下設(shè)置:
import { parse, stringify } from 'lossless-json' // 對number值進(jìn)行轉(zhuǎn)換,如果不用這個(gè)方法,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 } })
如果你跑了項(xiàng)目,你可能會發(fā)現(xiàn),項(xiàng)目無法正常跑起來,并且在瀏覽器控制臺console欄,發(fā)現(xiàn)Uncaught SyntaxError報(bào)錯(cuò),如下圖
按照這英文提示,結(jié)合我們引用時(shí)候直接從對象中取parse, stringify方法 ,我們不難理解其含義,lossless-json并沒有向我們提供一個(gè)默認(rèn)接口對象,讓我們通過一個(gè)對象直接調(diào)用parse, stringify方法,而是只暴露了parse, stringify。
就像我從json-bigint源碼中找到的,
紅框框選的這部分,在lossless-json中并沒有,所以才出現(xiàn)了上面的報(bào)錯(cuò)。
關(guān)于在下載lossless-json引入使用報(bào)錯(cuò)的情況,我文中開始就已經(jīng)提到,我就不再贅述,或許你遇都遇不到。
即使你真的遇到了,可以嘗試重裝,或者使用nvm切換node環(huán)境,再或者降級下載的版本,看能不能下載后使用吧,具體什么原因?qū)е碌?,我是沒有興趣慢慢嘗試和研究的,如果你有興趣,可以去查找資料或者問lossless-json的作者,我就不再這里繼續(xù)啰嗦了啊。
最后
以上就是Vue項(xiàng)目中解決數(shù)字精度丟失問題的詳細(xì)內(nèi)容,更多關(guān)于Vue數(shù)字精度丟失的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue 獲取url參數(shù)、get參數(shù)返回?cái)?shù)組的操作
這篇文章主要介紹了vue 獲取url參數(shù)、get參數(shù)返回?cái)?shù)組的操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-11-11vue和H5 draggable實(shí)現(xiàn)拖拽并替換效果
這篇文章主要為大家詳細(xì)介紹了vue和H5 draggable實(shí)現(xiàn)拖拽并替換效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-07-07VUE?el-table列表搜索功能純前端實(shí)現(xiàn)方法
Vue表搜索是指在Vue應(yīng)用中實(shí)現(xiàn)對表格數(shù)據(jù)的搜索功能,下面這篇文章主要給大家介紹了關(guān)于VUE?el-table列表搜索功能純前端實(shí)現(xiàn)的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-09-09