平臺(tái)型頁(yè)面處理大量數(shù)據(jù)渲染方案之IndexDB

? ? ? ? 哈嘍各位老鐵,賦閑在家的幾天又鼓搗了個(gè)新知識(shí)點(diǎn),迫不及待拿出來(lái)與各位分享。??

? ? ? ? 最近老菜鳥碰到一個(gè)比較神奇的面試題,場(chǎng)景是一個(gè)列表頁(yè),如果服務(wù)端的接口返回的數(shù)據(jù)有1000多個(gè)(就是這么長(zhǎng),沒(méi)做分頁(yè),不要做分散思考),如何能保證瀏覽器渲染流暢度。

? ? ? ? 心聲:嘖嘖,多么奇葩的場(chǎng)景,服務(wù)沒(méi)做好的事情丟給前端來(lái)搞。但是情況就是這么個(gè)情況。??

? ? ? ? 其實(shí)如果這個(gè)問(wèn)題不糾結(jié)在把任務(wù)都交給前端的話,我們有諸多良好的解決方案,比方說(shuō)加BFF層,GraphQL,或者給后端小哥買奶茶,讓兄弟給加個(gè)分頁(yè)也不難~

? ? ? ? 但是~沒(méi)有如果,就是前端er來(lái)搞,那么身為一個(gè)優(yōu)秀前端工程師的你要怎么辦呢?

? ? ? ? 我一開始的思路就是要么滾動(dòng)?懶加載?那么加載過(guò)程中后續(xù)的數(shù)據(jù)從哪里來(lái)呢,是不是還要后端老鐵做分頁(yè)?如果我們一口氣把這些數(shù)據(jù)都變成dom渲染到頁(yè)面上,那么瀏覽器勢(shì)必會(huì)卡頓,這是由于生成渲染樹時(shí)如果一幀沒(méi)渲染完,會(huì)等待下一幀,具體請(qǐng)參考:為什么JS同時(shí)讓百八十個(gè)DOM動(dòng)起來(lái)會(huì)卡頓

? ? ? ? 那么解決這個(gè)問(wèn)題就有思路了:

我通過(guò)api接口拿到大量數(shù)據(jù),把它存在本地,由前端逐步控制渲染就完了~

????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ————前端裝逼架構(gòu)師 Yubble

? ? ? ? 具體流程圖如下:

? ? ? ? 想必在下的這個(gè)泳道圖應(yīng)該能說(shuō)明了吧,就是要把大量數(shù)據(jù)先都拿到端上、存起來(lái)(這個(gè)環(huán)節(jié)不涉及到瀏覽器渲染,所以不會(huì)出現(xiàn)瀏覽器卡頓的問(wèn)題),之后少量的提取出來(lái),做頁(yè)面的Dom渲染。

? ? ? ? 需要注意點(diǎn):

? ? ? ? 既然是龐大數(shù)據(jù),存在哪兒?

????????存在localStorage里?小點(diǎn)吧,而且localStorage經(jīng)常存的是什么,持久化用戶信息吧,一個(gè)大個(gè)兒的cookie罷了,只是不能帶上它做前后端通信。且并沒(méi)有結(jié)構(gòu)化讀寫能力,存?zhèn)€龐大的數(shù)據(jù)結(jié)構(gòu)進(jìn)去獲取的時(shí)候又是個(gè)麻煩事。

? ? ? ? 這時(shí)候就到了我們的主角IndexDB登場(chǎng)了,結(jié)構(gòu)化存儲(chǔ),非關(guān)系型數(shù)據(jù)庫(kù),類似mongoDB,直接鍵值對(duì)存在,對(duì)于前端JS是天然優(yōu)勢(shì)。

? ? ? ? 到這里概要都講完了,剩下就是具體實(shí)施了~


? ? ? ? 我們實(shí)驗(yàn)的步驟有以下幾點(diǎn):

? ? ? ? 1、搭建能吐出大量數(shù)據(jù)的服務(wù)器

? ? ? ? 2、用一個(gè)前端頁(yè)面去承載這些數(shù)據(jù)繪制Dom,觀察頁(yè)面卡頓情況

? ? ? ? 3、編寫前端數(shù)據(jù)庫(kù)控件,完成新建數(shù)據(jù)庫(kù),添加數(shù)據(jù),查詢數(shù)據(jù)的功能

? ? ? ? 4、嘗試分批從前端數(shù)據(jù)庫(kù)提取數(shù)據(jù),查看頁(yè)面渲染時(shí)卡頓情況

? ? ? ? 5、完事吃夜宵

? ? ? ? 第一步,我們先搭建服務(wù),這里采用node,并隨機(jī)生成10000條數(shù)據(jù),每一條數(shù)據(jù)都有三個(gè)內(nèi)容,分別是id,產(chǎn)品名稱,產(chǎn)品價(jià)格,代碼如下:

注意處理下跨域哈

? ? ????第二步,隨便寫個(gè)頁(yè)面請(qǐng)求它,放到頁(yè)面Dom上,看下渲染結(jié)果~

注意看我在點(diǎn)擊完加載按鈕之后,明顯有一個(gè)加載中的時(shí)間

? ? ? ? 乍一看您會(huì)不會(huì)覺得是異步加載,接口反應(yīng)慢導(dǎo)致的,但是看接口返回其實(shí)一共才用了46毫秒,絕對(duì)毫秒級(jí)返回,且接口返回后頁(yè)面依舊顯示加載中文案,這將近一秒的加載中時(shí)間,其實(shí)全耗費(fèi)在了瀏覽器渲染上。(我是本地訪問(wèn),沒(méi)多少消耗)

? ? ? ? (由于老菜鳥寫的list結(jié)構(gòu)簡(jiǎn)單,內(nèi)容單一,所以渲染慢并不是太明顯,如果他們Dom層級(jí)嵌套多,內(nèi)容復(fù)雜的話效果會(huì)更明顯)

? ? ? ? 前端代碼如下:

dom結(jié)構(gòu)
js調(diào)用

????????為什么沒(méi)有顯示加載完畢呢,因?yàn)閖s中已經(jīng)拿到所有數(shù)據(jù)了,eventloop在跑到這個(gè)異步請(qǐng)求返回時(shí)data已經(jīng)完成數(shù)據(jù)承載了,下一次的eventloop tick的時(shí)候虛擬dom已經(jīng)備好了渲染list的狀態(tài),只不過(guò)瀏覽器重繪量太大,一時(shí)反應(yīng)不過(guò)來(lái),所以就停留在'加載中'。加油,我們已經(jīng)驗(yàn)證一半了~

? ? ? ? 第三步,編寫前端數(shù)據(jù)庫(kù),實(shí)現(xiàn)建庫(kù),添加內(nèi)容,查詢等功能

? ? ? ? 這一步我們就要上活兒了,這篇博文不是教學(xué)哈,主要還是展示調(diào)研成果為主,所以關(guān)于IndexDB的各類api還得各位客爺自行學(xué)習(xí),或者直接照著在下現(xiàn)成的敲一番也行,反正我也是找的網(wǎng)上現(xiàn)成的。??

? ? ? ? 首先我們新建一個(gè).js文件,用來(lái)放操作IndexDB的所有方法模塊,這里要是在實(shí)際開發(fā)中直接寫在業(yè)務(wù)中,你看組長(zhǎng)打不打你。

? ? ? ? 其中包括三個(gè)主要方法,打開數(shù)據(jù)庫(kù)(open),添加數(shù)據(jù)(inser),查詢數(shù)據(jù)(query)

? ? ? ? 大致結(jié)構(gòu)如下:

注意getStoreByTransaction是通過(guò)數(shù)據(jù)庫(kù)的'事務(wù)'搭一座橋,拿到數(shù)據(jù)庫(kù),才能對(duì)其增刪改查進(jìn)行操作。只將open方法暴露出去,因?yàn)椴樵兓虿迦氲那疤幔仨毾葘?shù)據(jù)庫(kù)打開,任務(wù)流不能亂哈。

? ? ? ? 我們來(lái)一起看看open方法

? ? ? ? 其中比較有價(jià)值的地方就在于IndexedDB.open()方法返回的請(qǐng)求對(duì)象request,它有三個(gè)回調(diào),分別是開啟成功(onsuccess),開啟失敗(onerror),以及新建庫(kù)之后的回調(diào)(onupgradeneeded)。

? ? ? ? 其中onupgradeneeded這個(gè)回調(diào)比較關(guān)鍵,數(shù)據(jù)庫(kù)版本更新或新建庫(kù)時(shí)就會(huì)運(yùn)行它,在這里可以看下是否有歷史的舊庫(kù),如果有就刪掉。然后我們開始創(chuàng)建商品表,并給與id為主鍵,咱們分別創(chuàng)建兩個(gè)索引,商品名稱和商品價(jià)格(就是代碼中的indexArr隊(duì)列)

? ? ? ? 而onsuccess中我們做的就很簡(jiǎn)單,將對(duì)于數(shù)據(jù)庫(kù)的操作api全部暴露出去,相當(dāng)于給了業(yè)務(wù)一個(gè)數(shù)據(jù)庫(kù)的遙控器。

? ? ? ? 我們?cè)賮?lái)看看插入方法怎么寫的

過(guò)于簡(jiǎn)單不解釋了吧

? ? ? ? 看下查詢方法

? ? ? ? 查詢方法里我們就要接觸到一個(gè)特別的概念了,即游標(biāo)(cursor)。

? ? ? ? 這個(gè)家伙事兒是數(shù)據(jù)查詢時(shí)必要的一個(gè)元素,試想一下,如果我們想要快速定位一條信息,除了要知曉信息詳細(xì)內(nèi)容外,通過(guò)隊(duì)列的下標(biāo)查找也是常用方案。所以他提供了IDBKeyRange,用于通過(guò)下標(biāo)或者具體的值來(lái)定位一條信息的游標(biāo)。

? ? ? ? 再通過(guò)openCursor這個(gè)方法打開這個(gè)游標(biāo),就可以在onsuccess這個(gè)回調(diào)中拿到這一條信息具體內(nèi)容。

? ? ? ? 如果還需要即系定位到它的下一條信息則直接在這個(gè)內(nèi)容對(duì)象上調(diào)用.continue()方法,因此可一口氣刷出一個(gè)游標(biāo)后置的N條信息結(jié)果。

? ? ? ? 如果客爺您之前有用過(guò)mongoDB,那么這一塊上手應(yīng)該很簡(jiǎn)單,沒(méi)用過(guò)的客爺也可以參照上圖查詢10條的邏輯自己敲一下,一目了然~

? ? ? ? 第四步,嘗試將數(shù)據(jù)全部灌入瀏覽器數(shù)據(jù)庫(kù),并分批渲染

此處加上了對(duì)于數(shù)據(jù)庫(kù)的幾處操作

? ? ? ? 我們來(lái)看下實(shí)驗(yàn)效果

? ? ? ? 此時(shí)我把數(shù)據(jù)全都拉下來(lái)存到IndexDB中,再?gòu)闹蝎@取10條信息渲染到頁(yè)面上,還是會(huì)有明顯加載中的閃現(xiàn),不過(guò)可以看到比之前流暢多了,如果數(shù)據(jù)結(jié)構(gòu)復(fù)雜,層級(jí)多的話,效果會(huì)更明顯。

? ? ? ? 畢竟操作的是大量結(jié)構(gòu)數(shù)據(jù)庫(kù),為避免阻塞,indexDB的操作還都放在了eventLoop的異步操作中,所以還是會(huì)有明顯異步痕跡,讓人能看到加載中的字樣。

? ? ? ? 我如果把上一頁(yè)、下一頁(yè)的切換加上,效果就更明顯了,基本上看不出延時(shí)效果

上一頁(yè)和下一頁(yè)邏輯

? ? ? ? 以上就是用IndexDB緩存前端大量數(shù)據(jù)的調(diào)研全紀(jì)錄,如果考慮到數(shù)據(jù)的實(shí)時(shí)性,就要考慮讓服務(wù)給數(shù)據(jù)加上版本控制,或者每次前端在頁(yè)面刷新時(shí)重新獲取一遍數(shù)據(jù)整體內(nèi)容。

? ? ? ? 總結(jié)下吧,IndexDB解決的最大問(wèn)題就是:頁(yè)面承載內(nèi)容太多時(shí),可以讓瀏覽器做一個(gè)緩沖,減少瀏覽器壓力。

? ? ? ? 以上就是我們對(duì)IndexDB的調(diào)研全過(guò)程了,希望各位客爺有所收獲。


? ? ? ? 這篇文章我調(diào)研、實(shí)驗(yàn)、編寫花了大概4、5天的時(shí)間,要單說(shuō)IndexDB起碼還能多寫兩篇,所以本文還是主要以調(diào)研為主,api的使用還是請(qǐng)各位客爺查看官方文檔,在此感謝下“熊的貓”的用IndexDB實(shí)現(xiàn)分頁(yè)一文。

? ? ? ? 寫完這篇博文讓我想起了之前面試的一位小哥,大概6、7年開發(fā)經(jīng)驗(yàn),但是多在功能實(shí)現(xiàn)層面,整體沒(méi)什么立體架構(gòu)思維,面試結(jié)束后垂頭喪氣~和我說(shuō)自己知識(shí)面不夠,一直寫業(yè)務(wù)沒(méi)人帶,沒(méi)什么成長(zhǎng)~

? ? ? ? 雖然最后的確gg了,但是我卻不是很贊同他的觀點(diǎn),我們所處的行業(yè)日新月異,工具框架過(guò)幾天出一個(gè),誰(shuí)也不能面面俱到,所以不能因?yàn)橐粌纱蚊嬖嚨檬Ф穸ㄗ约喊。荒苷f(shuō)面的這個(gè)崗位和候選人經(jīng)驗(yàn)及能力不是很匹配,一個(gè)做了3、4年架構(gòu)的工程師肯定沒(méi)有專心寫業(yè)務(wù)的同學(xué)開發(fā)快,一個(gè)只寫業(yè)務(wù)邏輯的工程師對(duì)架構(gòu)設(shè)計(jì)思維也一定是貧瘠的。

? ? ? ? 所以我還是固執(zhí)的認(rèn)為,沒(méi)有什么能力強(qiáng)弱,只是和當(dāng)前職位匹不匹配罷了。

? ? ? ? 畢竟能干這行的,沒(méi)有自學(xué)能力弱的~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容