2020-4-27 seo達(dá)人
無(wú)論是 pc 端還是移動(dòng)端,無(wú)可避免都會(huì)涉及到列表查詢有關(guān)的操作,但對(duì)于這兩種不同的設(shè)備,其列表查詢的最佳處理方式也是完全不同。
對(duì)于 pc 端列表查詢來(lái)說(shuō),前端通常是給與服務(wù)端當(dāng)前需要獲取的數(shù)據(jù)量(如 pageCount,limit 等參數(shù))以及所需要獲取數(shù)據(jù)的位置(如 pageSize,offset 等參數(shù))作為查詢條件。然后服務(wù)端然后返回?cái)?shù)據(jù)總數(shù),以及當(dāng)前數(shù)據(jù),前端再結(jié)合這些數(shù)據(jù)顯示頁(yè)面總數(shù)等信息。這里我稱為相對(duì)位置取數(shù)。
對(duì)于移動(dòng)端而言,沒(méi)有pc 端那么大的空間展示以及操作,所以基本上都會(huì)采用下拉取數(shù)這種方案。
那么我們?cè)谔幚硪苿?dòng)端列表查詢時(shí)候使用這種相對(duì)位置取數(shù)會(huì)有什么問(wèn)題呢?
通過(guò)相對(duì)位置取數(shù)會(huì)具有性能問(wèn)題,因?yàn)橐坏┦褂?offset 信息來(lái)獲取數(shù)據(jù),隨著頁(yè)數(shù)的增加,響應(yīng)速度也會(huì)變的越來(lái)越慢。因?yàn)樵跀?shù)據(jù)庫(kù)層面,我們每次所獲取的數(shù)據(jù)都是“從頭開(kāi)始第幾條”,每次我們都需要從第一條開(kāi)始計(jì)算,計(jì)算后舍棄前面的數(shù)據(jù),只取最后多條數(shù)據(jù)返回前端。
當(dāng)然了,對(duì)于相對(duì)位置取數(shù)來(lái)說(shuō),數(shù)據(jù)庫(kù)優(yōu)化是必然的,這里我就不多做贅述了。對(duì)于前端開(kāi)發(fā)來(lái)說(shuō),優(yōu)秀的的查詢條件設(shè)計(jì)可以在一定方面解決此問(wèn)題。
事實(shí)上,對(duì)于一個(gè)實(shí)際運(yùn)行的項(xiàng)目而言,數(shù)據(jù)更新才是常態(tài),如果數(shù)據(jù)更新的頻率很高或者你在當(dāng)前頁(yè)停留的時(shí)間過(guò)久的話,會(huì)導(dǎo)致當(dāng)前獲取的數(shù)據(jù)出現(xiàn)一定的偏差。
例如:當(dāng)你在獲取最開(kāi)始的 20 條數(shù)據(jù)后,正準(zhǔn)備獲取緊接著的后 20 條數(shù)據(jù)時(shí),在這段時(shí)間內(nèi) ,發(fā)生了數(shù)據(jù)增加,此時(shí)移動(dòng)端列表就可能會(huì)出現(xiàn)重復(fù)數(shù)據(jù)。雖然這個(gè)問(wèn)題在 pc 端也存在,但是 pc 端只會(huì)展示當(dāng)前頁(yè)的信息,這樣就避免了該問(wèn)題所帶來(lái)的負(fù)面影響。
我們?cè)谏厦娴膯?wèn)題中說(shuō)明了,移動(dòng)端下拉加載中使用相對(duì)位置查詢?nèi)?shù)是有問(wèn)題的。
那么,如果當(dāng)前不能迅速結(jié)合前后端進(jìn)行修改 api 的情況下,當(dāng)服務(wù)端傳遞過(guò)來(lái)的數(shù)據(jù)與用戶想要得的數(shù)據(jù)不一致,我們必須在前端進(jìn)行處理,至少處理數(shù)據(jù)重復(fù)問(wèn)題所帶來(lái)的負(fù)面影響。
因?yàn)楫?dāng)前分頁(yè)請(qǐng)求時(shí)無(wú)狀態(tài)的。在分頁(yè)取到數(shù)據(jù)之后前端可以對(duì)取得的數(shù)據(jù)進(jìn)行過(guò)濾,過(guò)濾掉當(dāng)前頁(yè)面已經(jīng)存在的 key(例如 id 等能夠確定的唯一鍵)。
通過(guò)這種處理方式,我們至少可以保證當(dāng)前用戶看到的數(shù)據(jù)不會(huì)出現(xiàn)重復(fù)。同時(shí)當(dāng)列表數(shù)據(jù)可以編輯修改的時(shí)候,也不會(huì)出現(xiàn)因?yàn)?key 值相同而導(dǎo)致數(shù)據(jù)錯(cuò)亂。
如果不使用相對(duì)位置獲取數(shù)據(jù),前端可以利用當(dāng)前列表中的最后一條數(shù)據(jù)作為請(qǐng)求源參數(shù)。前端事先記錄最后一條數(shù)據(jù)的信息。例如當(dāng)前的排序條件為創(chuàng)建時(shí)間,那么記錄最后一條數(shù)據(jù)的創(chuàng)建時(shí)間為主查詢條件(如果列表對(duì)應(yīng)的數(shù)據(jù)不屬于個(gè)人,可能創(chuàng)建時(shí)間不能唯一決定當(dāng)前數(shù)據(jù)位置,同時(shí)還需要添加 ID 等信息作為次要查詢條件)。
當(dāng)我們使用絕對(duì)位置獲取數(shù)據(jù)時(shí)候,雖然我們無(wú)法提供類似于從第 1 頁(yè)直接跳轉(zhuǎn) 100 頁(yè)的查詢請(qǐng)求,但對(duì)于下拉加載這種類型的請(qǐng)求,我們不必?fù)?dān)心性能以及數(shù)據(jù)重復(fù)顯示的問(wèn)題。
對(duì)于相對(duì)位置取數(shù)來(lái)說(shuō),前端可以根據(jù)返回?cái)?shù)據(jù)的總數(shù)來(lái)判斷。但當(dāng)使用絕對(duì)位置取數(shù)時(shí),即使獲取數(shù)據(jù)總數(shù),也無(wú)法判斷當(dāng)前查詢是否存在后續(xù)數(shù)據(jù)。
從服務(wù)器端實(shí)現(xiàn)的角度來(lái)說(shuō),當(dāng)用戶想要得到 20 條數(shù)據(jù)時(shí)候,服務(wù)端如果僅僅只向數(shù)據(jù)庫(kù)請(qǐng)求 20 條數(shù)據(jù),是無(wú)法得知是否有后續(xù)數(shù)據(jù)的。服務(wù)端可以嘗試獲取當(dāng)前請(qǐng)求的數(shù)據(jù)條數(shù) + 1, 如向數(shù)據(jù)庫(kù)請(qǐng)求 21 條數(shù)據(jù),如果成功獲得 21 條數(shù)據(jù),則說(shuō)明至少存在著 1 條后續(xù)數(shù)據(jù),這時(shí)候,我們就可以返回 20 條數(shù)據(jù)以及具有后續(xù)數(shù)據(jù)的信息。但如果我們請(qǐng)求 21 條數(shù)據(jù)卻僅僅只能獲取 20 條數(shù)據(jù)(及以下),則說(shuō)明沒(méi)有后續(xù)數(shù)據(jù)。
如可以通過(guò) “hasMore” 字段來(lái)表示是否能夠繼續(xù)下拉加載的信息。
{ data: [], hasMore: true }
事實(shí)上,前面我們已經(jīng)解決了移動(dòng)端處理列表查詢的問(wèn)題。但是我們做的還不夠好,前端還需要結(jié)合排序條件來(lái)處理并提供請(qǐng)求參數(shù),這個(gè)操作對(duì)于前端來(lái)說(shuō)也是一種負(fù)擔(dān)。那么我們就聊一下 HATEOAS 。
HATEOAS (Hypermedia As The Engine Of Application State, 超媒體即應(yīng)用狀態(tài)引起) 這個(gè)概念最早出現(xiàn)在 Roy Fielding 的論文中。REST 設(shè)計(jì)級(jí)別如下所示:
HATEOAS 會(huì)在 API 返回的數(shù)據(jù)中添加下一步要執(zhí)行的行為,要獲取的數(shù)據(jù)等 URI 的鏈接信息??蛻舳酥灰@取這些信息以及行為鏈接,就可以根據(jù)這些信息進(jìn)行接下來(lái)的操作。
對(duì)于當(dāng)前的請(qǐng)求來(lái)說(shuō),服務(wù)端可以直接返回下一頁(yè)的信息,如
{ data: [], hasMore: true, nextPageParams: {}
}
服務(wù)端如此傳遞數(shù)據(jù),前端就不需要對(duì)其進(jìn)行多余的請(qǐng)求處理,如果當(dāng)前沒(méi)有修改之前的查詢以及排序條件,則只需要直接返回 “nextPageParams” 作為下一頁(yè)的查詢條件即可。
這樣做的好處不但符合 REST LEVEL 3,同時(shí)也減輕了前端的心智模型。前端無(wú)需配置下一頁(yè)請(qǐng)求參數(shù)。只需要在最開(kāi)始查詢的時(shí)候提供查詢條件即可。
當(dāng)然,如果前端已經(jīng)實(shí)現(xiàn)了所有排序添加以及查詢條件由服務(wù)端提供,前端僅僅提供組件,那么該方案更能體現(xiàn)優(yōu)勢(shì)。 前端是不需要知道當(dāng)前業(yè)務(wù)究竟需要什么查詢條件,自然也不需要根據(jù)查詢條件來(lái)組織下一頁(yè)的條件。同時(shí),該方案的輸入和輸出都由后端提供,當(dāng)涉及到業(yè)務(wù)替換( 查詢條件,排序條件修改)時(shí)候,前端無(wú)需任何修改便可以直接替換和使用。
一旦涉及到移動(dòng)端請(qǐng)求,不可避免的會(huì)有網(wǎng)絡(luò)問(wèn)題,當(dāng)用戶在火車或者偏遠(yuǎn)地區(qū)時(shí)候,一旦下拉就會(huì)涉及取數(shù),但是當(dāng)前數(shù)據(jù)沒(méi)有返回之前,用戶多次下拉可能會(huì)有多次取數(shù)請(qǐng)求,雖然前端可以結(jié)合 key 使得渲染不出錯(cuò),但是還是會(huì)在緩慢的網(wǎng)絡(luò)下請(qǐng)求多次,無(wú)疑雪上加霜。這時(shí)候我們需要增加條件變量 loading。
偽代碼如下所示:
// 查詢 function search(cond) {
loading = true api.then(res => {
loading = false }).catch(err => {
loading = false })
} // 獲取下一頁(yè)數(shù)據(jù) function queryNextPage() { if (!nextPageParams) return if (!loading) return search(nextPageParams)
}
藍(lán)藍(lán)設(shè)計(jì)的小編 http://tweetduck.com