無限滾動列表,顧名思義,是可以無限滾動的列表(願意是指那些可以不斷緩衝加載新數據的列表的)。可是,咱們真的須要這樣一個列表嗎?在PC端,瀏覽器的性能其實已經可以知足海量dom節點的渲染刷新(筆者通過簡單的測試,1w+的節點並無很明顯的卡頓),可是一樣的dom數量在移動端卻不行,在dom結構合理的狀況下,可以保持在2K+的dom數量已經是一個很不錯的列表了,可是dom數量再進一步增長就會明顯影響頁面的渲染效率,也正由於此緣由,纔有了此文關於無限列表的一些思考和探索。react
首先,爲何會卡頓?由於dom數量的絕對值上的不斷增多,在後續的交互上(如滾動,局部更新)都會致使頁面渲染性能的降低。ios
那麼,應該如何解決?既然是dom數量過多引發的,最簡單也最有效的方法天然是減小dom數量。在jQuery時代,筆者便見過一種思路(其實本質實現至今也沒有太大的變化):git
一、只渲染特定幾屏的數據,將多餘的數據緩存在內存中,並不直接實例化到dom tree上,減小dom的絕對數量;github
二、監聽滾動事件,在滾動的時候動態的更新dom,讓用戶在視覺上看不出區別;api
jQuery的實現因爲年代久遠,已經找不太到了,不過筆者在react社區找到了一個比較好的實現:react-virtualized,值得一提的是,它還將咱們的滾動場景區分爲了viewport內的局部滾動,和基於viewport的滾動,前者至關於在頁面中開闢了一塊獨立的滾動區域,屬於內部滾動(和iscroll的滾動很相似,順帶一提iscroll也給出了iscroll-infinite的解決方案);然後者則把滾動做爲了window滾動的一部分(對於移動端而言,在非模態窗的場景下這種滾動更常見,共用一個滾動條也不容易引發用戶的誤操做)。由於筆者主要的使用場景是後者,而iscroll的解決方案雖然也能做用於頁面維度的滾動,可是它須要徹底替換scroll事件(至關於頁面沒有了scroll事件,只有touchmove事件;且此種頁面在內部滾動和全局滾動的實現上有不小的難度),因此筆者在無限滾動列表上並無使用iscroll的方案。瀏覽器
不過,業內雖然有現成的解決方案當然是好事,可是每種方案都有本身的特性,並不必定都合適,以react-virtualized來講,它的特性在pc上算是一個頗有趣也很不錯的解決方案了:緩存
一、它構造一個「足夠大」的容器來再現滾動條的實際數值;dom
二、它使用絕對定位來不斷跟隨滾動事件,改變元素的位置,幾乎完美還原了正常列表的視覺,並且不管dom再多也不會卡頓;性能
不過,與此同時,它也有一些不足:測試
一、由於使用了scroll事件,某種程度上,就註定了在ios上的不足(ios scroll時會阻塞js執行),加上它的緩衝區實際上是單向的(雖然這也體現了做者想盡量節省dom的願景),致使用戶若是上下來回滾動,則很容易看到白屏;
二、因爲絕對定位的「小技巧」,它要求在組件渲染之初就必須知道每一行元素的高度,可是這個看起來不起眼的小操做,卻很大的影響了開發的體驗(很不湊巧的筆者使用的無限列表的場景,不少狀況下列表元素的高度都不能預先知道。。)
也基於以上緣由,雖然react-virtualized是很不錯的解決方案了,可是筆者最終沒有采用。不過,在普遍借鑑了各大廠的實現以後,筆者發現,手淘列表頁的實現,最簡單也最有效,那麼簡單說下實現思路:
一、它引入了分頁的概念,在用戶不斷刷新增長頁面長度的同時,它將若干元素分爲一頁;
二、而後在滾動的時候計算當前滾動到了具體的哪一頁,將「多餘」(不須要顯示,也不須要做爲緩衝顯示)的頁的高度取出,直接賦值在容器上,而後將容器內全部元素置空;
三、當「當前頁」發生變化時,動態將須要顯示「空頁」從新加上元素。
其實這一解決方案也有反覆操做dom的性能問題,並且它並非dom數量優化上的最優解,可是結合筆者的實際使用場景,並且結合考慮到對業務同窗的api友好等各方面的,筆者最終也選擇了這一實現,雖然各方面性能不是最好,可是在筆者當前的使用場景中,卻最是有效的。
簡單的小結一下,其實有關無限列表的實現有不少種方案,使用原生scroll事件的痛點在於ios的js阻塞問題以及如何巧妙的設計緩衝區,而使用transform模擬滾動的痛點則是有具體場景的限制和總體的重構成本,兩種方案各有千秋,具體使用還須要看具體的使用場景,因此,聰明的你,告訴我?咱們須要無線滾動列表麼?