一文搞懂長列表優化和虛擬滾動

寫在前面

還 ⭐ 的基於 react-hooks 和 react-virtualized 寫的虛擬滾動搭配上拉下滑加載的 scroll 組件
🌟🌟 github.com/DavidWong97… 🌟🌟
寫出來的小玩意可能不是特別完美,若是能給大夥們提供到思路或者幫助,實在不勝榮幸
也但願大夥們給個 star 啦~react

antd中的list-view

相信用過 antd-mobile 的小夥伴都知道里面的 ListView 組件
組件還算是能知足不少須要的
但遇到數據量大時可能就不太夠用了
並且一些不熟悉 useMemo 的小夥伴對於 ListView 的優化可能會不知所措
因此我就封裝了一個不須要本身優化 renderRow,又能作到虛擬滾動,又能知足 ListView 基本須要的組件
接下來就一步步拆解,如何實現這個組件😈
複製代碼

什麼是虛擬滾動

1. 背景
    當遇到數據量大的列表渲染,如10萬條數據,一次性渲染十萬個元素,內存消耗確定是裂開的
    這時候考慮怎麼展現完列表,又能優化性能提高體驗 —— 虛擬滾動
2. 效果描述
    建立一個容器,容器內部展現固定數量的數據,經過先後索引肯定渲染數據的哪一個區間
複製代碼

react-virtualized原理剖析

1. 找出渲染區間
        a. startIndex: Math.max(0, startIndex - threshold),
        b. stopIndex: Math.min(rowCount - 1, stopIndex + threshold)
        c. threshold爲列表項目數,默認15
2. 觸發渲染
    _invokeRendered【渲染列表】
        a. componentDidMount
        b. componentDidUpdate
3. 對比後渲染
    每次渲染會緩存上一次的屬性,若屬性【如index】不同,從新渲染
複製代碼

長列表實現

1. 構成:refresh + virtualScroll + loadMore
2. 狀態控制 STATS
    a. init - 初始狀態
    b. dragging - 拖動中
    c. pre-refresh - 拖動達到最大限制,刷新狀態就緒
    d. not-enough - 拖動未達到最大限制
    e. refreshing - 鬆手,正在刷新
    f. success - 刷新成功
3. refresh 實現
    a. onTouchStart
        記錄startY,若狀態爲refreshing或success,不執行
        若scrollTop不爲0,不執行
    b. onTouchMove
        計算logo的offset -> 當前pageY - startY
        根據條件設置offset
            ① 0 < offset && offset <= 下拉最大限制,設爲計算得出的offset
            ② offset < 0,設爲0
            ③ offset > 下拉最大限制,設爲下拉最大限制
        
        若 offset >= 下拉最大限制,狀態設爲pre-refresh
        不然若 offset < 下拉最大限制,狀態設爲dragging
    c. onTouchEnd
        若STATS爲 pre-refresh
            有傳入回調onPullDown則調用,狀態變動爲 refreshing
            回調執行完,狀態變動爲 success,延遲後變爲init
        不然
            狀態變動爲 not-enough,延遲後變爲init
4. virtualScroll 實現
    a. 渲染傳入的自定義組件children
    b. 若傳入data有數據,渲染vList
5. loadMore 實現
    vList 中的 rowRenderer
        ① 傳入對應的參數給 row 組件並渲染
        ② 根據狀態渲染loading和沒有更多的展現
6. onScroll回調封裝一層
    a. 有傳入onScroll,執行回調onScroll
    b. 有傳入onPullUp
        若 clientHeight + scrollTop === scrollHeight
        則到達底部,執行回調onPullUp
複製代碼

長列表rowRender優化

  1. 若是不做useMemo優化,列表數據其中一項只要更改,列表全部項都會從新渲染,形成性能浪費
  2. 對於傳入的rowRender,須要獨立聲明一個組件包着再做渲染
  3. 判斷的方式這裏有些取巧了,直接JSON.stringify數據做判斷
const Row = ({ row, data, index, info }:any) => {
  return useMemo(() => row({ data: data, index, info }), [JSON.stringify(data[index])])
}
複製代碼

寫在後面

看到這裏的小夥伴,有沒有以爲實現個長列表其實也很簡單呢~
文中的解析再對照着項目中的代碼看,相信你必定有所收穫
還有~~~別忘了👍~⭐~👍~⭐~👍~⭐~👍~⭐~👍~⭐~👍~⭐~👍~⭐~👍~⭐~👍~⭐~
複製代碼
相關文章
相關標籤/搜索