【W(wǎng)eb前端開(kāi)發(fā)】偷偷告訴你,我們項(xiàng)目里的進(jìn)度條,全都是假的!
當(dāng)前位置:點(diǎn)晴教程→知識(shí)管理交流
→『 技術(shù)文檔交流 』
導(dǎo)讀 這篇文章主要探討了項(xiàng)目中假進(jìn)度條的實(shí)現(xiàn)。先介紹需求背景,然后調(diào)研了 NProgress 和 fake-progress 兩個(gè)方案,分析其源碼和特點(diǎn)。接著提到因現(xiàn)有方案的不足,作者萌生封裝自己的 hook(useFakeProgress)的想法,確定方案和入?yún)ⅲo出實(shí)現(xiàn)細(xì)節(jié)和示例,強(qiáng)調(diào)可根據(jù)業(yè)務(wù)定制。 扯皮最近接到了一個(gè)需求:前端點(diǎn)擊按鈕觸發(fā)某個(gè)任務(wù)并開(kāi)啟輪詢獲取任務(wù)進(jìn)度,直至 100% 任務(wù)完成后給予用戶提示 這個(gè)業(yè)務(wù)場(chǎng)景還挺常見(jiàn)的,但是突然上周后端聯(lián)系到我說(shuō)現(xiàn)在的效果有點(diǎn)差,之前都是小任務(wù)那進(jìn)度條展示還挺不錯(cuò)的,現(xiàn)在有了一些大任務(wù)且會(huì)存在排隊(duì)阻塞的情況,就導(dǎo)致視圖上經(jīng)常卡 0% 排隊(duì),用戶體驗(yàn)太差了,問(wèn)能不能在剛開(kāi)始的時(shí)候做個(gè)假進(jìn)度先讓進(jìn)度條跑起來(lái)?? 因此就有了這篇文章,簡(jiǎn)單做一下技術(shù)調(diào)研以及在項(xiàng)目中的應(yīng)用 正文其實(shí)假進(jìn)度條也不難做,無(wú)非是輪詢的時(shí)候我們自己做一個(gè)隨機(jī)的自增,讓它卡到 99% 等待后端真實(shí)進(jìn)度完成后再結(jié)束 只不過(guò)還是想調(diào)研一下看看市面上有沒(méi)有一些成熟的方案并去扒一下它們的源碼?? NProgress首先當(dāng)我聽(tīng)到這里的需求后第一時(shí)間想到的就是它:rstacruz/nprogress: For slim progress bars like on YouTube, Medium, etc 記得大學(xué)期間做的一些中后臺(tái)系統(tǒng)基本都少不了路由跳轉(zhuǎn)時(shí)的頂部進(jìn)度條加載,那時(shí)候就有了解到 NProgress,它的使用方式也很簡(jiǎn)單,完全手控:NProgress: slim progress bars in JavaScript,去文檔里玩一下就知道了 視圖呈現(xiàn)的效果就是如果你不手動(dòng)結(jié)束那它就會(huì)一直緩慢前進(jìn)卡死 99% ,挺符合我們這里的需求,可以去扒一下它內(nèi)部進(jìn)度計(jì)算相關(guān)的邏輯 NProgress 的內(nèi)容實(shí)際上比較少,源碼拉下來(lái)可以看到主要都在這一個(gè) JS 文件里了: 需要注意的是我們看的是這個(gè)版本:rstacruz/nprogress at v0.2.0,master 分支與 npm 安裝的 0.2.0 內(nèi)部實(shí)現(xiàn)還是有些差別的 我們這里不關(guān)注它的樣式相關(guān)計(jì)算,主要來(lái)看看對(duì)進(jìn)度的控制,直奔 start 方法: 還是比較清晰的,這里的 set 方法里有一大堆設(shè)置動(dòng)畫(huà)樣式邏輯都被我剪掉了,關(guān)于進(jìn)度相關(guān)的只有這些。相當(dāng)于利用 clamp 來(lái)做一個(gè)夾層,因?yàn)槌跏歼M(jìn)來(lái)的 n 為 null,所以經(jīng)過(guò)處理后進(jìn)度變?yōu)?0.08 再回到 start 的邏輯,其中 看來(lái)這里就是進(jìn)度控制的核心邏輯了, 而在 最后再調(diào)用 當(dāng)然
即根據(jù)當(dāng)前進(jìn)度 n 計(jì)算剩余進(jìn)度,再隨機(jī)生成自增值 再來(lái)看 按理來(lái)說(shuō)直接將進(jìn)度設(shè)置為 1 就行,但它以鏈?zhǔn)秸{(diào)用 而這里 一句話總結(jié)一下 NProgress 的進(jìn)度計(jì)算邏輯:隨機(jī)值自增,最大值限制 但是因?yàn)?NProgress 與進(jìn)度條樣式強(qiáng)綁定,我們肯定是沒(méi)法直接用的 fake-progress至于 fake-progress 則是我在調(diào)研期間直接搜關(guān)鍵詞搜出來(lái)的??:piercus/fake-progress: Fake a progress bar using an exponential progress function 很明顯看介紹就是干這個(gè)事的,而且還十分專業(yè),引入數(shù)學(xué)函數(shù)展示假進(jìn)度條效果,具有說(shuō)服力: 所以我們項(xiàng)目中其實(shí)就是用的這個(gè)包,只不過(guò)和 NProgress 類似,兩個(gè)包都比較老了,瞟一眼源碼發(fā)現(xiàn)都是老 ES5 了?? 因?yàn)槲覀冺?xiàng)目中用的是 React,這里給出 React 的 demo 吧,為了編寫(xiě)方便使用了幾個(gè) ahooks 里的 hook: 其實(shí)使用方法上與 NProgress 都類似,不過(guò)兩者都屬于通用的工具庫(kù)不依賴其他框架,所以像視圖渲染都需要自己手動(dòng)來(lái)做 注意實(shí)例化中的傳參 因?yàn)椴簧婕暗綐邮剑琭ake-progress 源碼更簡(jiǎn)單,核心就在這里: 下方的數(shù)學(xué)公式就是介紹圖中展示的,只能說(shuō)剛看到這部分內(nèi)容是真的是死去的數(shù)學(xué)只是突然又開(kāi)始攻擊我??,寫(xiě)了那么多函數(shù),數(shù)學(xué)函數(shù)是啥都快忘了 我們來(lái)簡(jiǎn)單分析一下這個(gè)函數(shù) 那假如這里改成 exp(-x) 呢?有那味了,以前應(yīng)該是有一個(gè)類似的公式 與 圖像效果是關(guān)于 y 軸對(duì)稱,好像是有些特殊的不符合這個(gè)規(guī)律???反正大部分都是滿足的 OK,那我們繼續(xù)進(jìn)行轉(zhuǎn)換,看看 -exp(-x) 的效果 同樣有個(gè)公式 與 圖像效果是關(guān)于 x 軸對(duì)稱: 初見(jiàn)端倪,不知道你們有沒(méi)有注意 -exp(-x) 最終呈現(xiàn)的圖像是無(wú)限接近于 x 軸的,也就是 0: 那有了??,假如我再給它加個(gè) 1 呢?它不就無(wú)限接近于 1 了,即 -exp(-x) + 1,這其實(shí)就是 fake-progress 里公式的由來(lái): 但你會(huì)發(fā)現(xiàn)如果 x 按 1 遞增就很快進(jìn)度就接近于 1 了,所以有了
所以 fake-progress 的核心原理是借助數(shù)學(xué)函數(shù),以函數(shù)值無(wú)限接近于 1 來(lái)實(shí)現(xiàn)假進(jìn)度條,但是這種實(shí)現(xiàn)有一個(gè) bug,可以看我提的這個(gè) issues,不過(guò)看這個(gè)包的更新時(shí)間感覺(jué)作者也不會(huì)管了??: bug: progress may reach 100% · Issue #7 · piercus/fake-progress useFakeProgress雖然我們現(xiàn)在項(xiàng)目中使用的是 fake-progress,但是個(gè)人感覺(jué)用起來(lái)十分雞肋,而且上面的 bug 也需要自己手動(dòng)兼容,因此萌生出自己封裝一個(gè) hook 的想法,讓它更符合業(yè)務(wù)場(chǎng)景 首先我們確定一下進(jìn)度計(jì)算方案,這里我毫不猶豫選擇的是 NProgress 隨機(jī)值增長(zhǎng)方案,為什么?因?yàn)榉奖阌脩糇远x 而且 NProgress 相比于 fake-progress 有一個(gè)巨大優(yōu)勢(shì):手動(dòng) set 進(jìn)度后仍然保持進(jìn)度正常自動(dòng)遞增 這點(diǎn)在 fake-progress 中實(shí)現(xiàn)是比較困難的,因?yàn)槟銦o(wú)法保證手動(dòng) set 的進(jìn)度是在這個(gè)函數(shù)曲線上,相當(dāng)于給出函數(shù) y 值反推 x 值,根據(jù)反推的 x 值再進(jìn)行遞增,想想都麻煩 確定好方案后我們來(lái)看下入?yún)桑瑓⒖?NProgress 我定義了這幾個(gè)配置項(xiàng): 這里我簡(jiǎn)單解釋一下 rerender 和 amount 配置: 實(shí)際上在封裝這個(gè) hook 的時(shí)候我一直在糾結(jié)這里的 progress 到底是 state 還是 ref,因?yàn)榇蠖鄶?shù)場(chǎng)景下 hook 內(nèi)部通過(guò)輪詢定時(shí)器更新進(jìn)度,而真實(shí)業(yè)務(wù)代碼中也會(huì)開(kāi)啟定時(shí)器去輪詢監(jiān)聽(tīng)業(yè)務(wù)接口的 所以如果寫(xiě)死為 state,那這個(gè)場(chǎng)景 hook 內(nèi)部的每次更新 render 是沒(méi)必要的,但是假如用戶又想只是使用假進(jìn)度展示,沒(méi)有后端業(yè)務(wù)接口呢? 思來(lái)想去其實(shí)完全可以放權(quán)給用戶進(jìn)行配置,因?yàn)?nbsp;state = ref + update,統(tǒng)一使用 ref,用戶配置 rerender 時(shí)我們?cè)诿看胃聲r(shí) update 即可 至于 amount 我是希望放權(quán)給用戶進(jìn)行自定義遞增值,你可以配置成一個(gè)固定值也可以配置成隨機(jī)值,更可以像 NProgress master 分支下這樣根據(jù)當(dāng)前進(jìn)度來(lái)控制自增,反正以函數(shù)參數(shù)的形式能夠拿到當(dāng)前的 progress: 至于實(shí)現(xiàn)細(xì)節(jié)就不再講述了,實(shí)際上就是輪詢定時(shí)器沒(méi)什么復(fù)雜的東西,直接上源碼了:
這里需要補(bǔ)充一個(gè)細(xì)節(jié),在返回值里使用的是 progressDataRef 是 formatter 后的結(jié)果為 string 類型,如果用戶想要獲取原 number 的 progress,可以使用最下面提供的 get 方法拿 progressRef 值 一個(gè) demo 看看效果,感覺(jué)還可以: 當(dāng)然由于直接返回了 ref,為了防止用戶篡改可以再上一層代理劫持,我們就省略了 這也算一個(gè)工具偏業(yè)務(wù)的 hook,可以根據(jù)自己的業(yè)務(wù)來(lái)進(jìn)行定制,這里很多細(xì)節(jié)都沒(méi)有補(bǔ)充只是一個(gè)示例罷了?? End以上就是這篇文章的內(nèi)容,記得上班之前還在想哪有那么多業(yè)務(wù)場(chǎng)景需要封裝自定義 hook,現(xiàn)在發(fā)現(xiàn)真的是各種奇葩需求都可以封裝,也算是豐富自己武器庫(kù)了... 作者:討厭吃香菜 鏈接:https://juejin.cn/post/7449307011710894080 來(lái)源:稀土掘金 著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。 該文章在 2025/1/10 15:08:28 編輯過(guò) |
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |