Haru Li
Frontend Developer
14 posts
一萬筆資料,只畫二十個節點
上一個 demo 講的是「不要一次載完」。這個講的是相反的一半:資料全都在記憶體裡了,但你不必全部畫出來。
發想:把兩件事分開
「列表很卡」通常會得到「用分頁或無限捲動」的建議。但那解的是資料量的問題。
有些情況資料本來就得全部在手上——要做排序、要算總計、要即時篩選。這時資料量沒得減,能減的是 DOM 節點數。
我想做的 demo 是把這兩件事切乾淨:兩側資料完全一樣,都是 10,000 筆在記憶體裡,唯一的差別是畫幾個節點。
測試:按下去會卡一下
左邊「全部渲染」預設是不畫的,要按按鈕才開始——因為它真的會讓畫面卡住,直接畫出來會影響整頁的其他 demo。
按下去那一下的停頓,就是瀏覽器在算 10,000 個節點的版面。
右邊「虛擬捲動」只畫可視範圍的大約 20 筆,捲動時靠位移把它們對到正確位置。捲起來完全沒有那個停頓。
兩側都會顯示實際的 DOM 節點數與首次渲染耗時。
理解:為什麼是節點數
瀏覽器對每個元素都要做這些事:算樣式、算版面位置、可能還要繪製與合成。這些成本大致隨節點數線性成長,但版面計算在節點互相影響時會更貴。
10,000 對 20,節點數差 500 倍。這個差距不只出現在第一次渲染——它持續影響捲動的流暢度和記憶體用量,因為那些節點一直在那裡。
虛擬捲動的做法:
- 用一個高度等於「總筆數 × 單列高度」的容器撐出正確的捲軸
- 根據目前捲動位置算出「現在應該看到第幾筆到第幾筆」
- 只渲染那個範圍,用
transform把它們推到對的位置
使用者看到的捲軸長度、捲動距離都跟真的畫一萬筆一樣,但 DOM 裡永遠只有二十幾個節點。
講解:什麼時候需要,什麼時候不用
需要的訊號:列表筆數是三位數以上、每一列的結構還不簡單(有圖、有多個欄位)、而且使用者真的會捲很長一段。
不需要的情況:幾十筆的列表。虛擬捲動要自己管高度、捲動位置、鍵盤操作與無障礙焦點,複雜度不低。為了 50 筆資料引進這些,是拿確定的維護成本換不確定的效能收益。
實務上多半不會自己實作,@tanstack/virtual 這類函式庫已經處理好變動列高、水平捲動、動態量測那些麻煩事。這個 demo 是手寫的,因為手寫才看得到機制。
這個 demo 可以在 效能實驗室 自己操作。
