網(wǎng)頁(yè)dom元素過(guò)多為什么會(huì)導(dǎo)致頁(yè)面卡頓
1、dom過(guò)多,占用過(guò)多的內(nèi)存。
2、操縱dom時(shí)觸發(fā)重排重繪,消耗瀏覽器性能。特別是每一次滾動(dòng)事件將會(huì)讓對(duì)應(yīng) DOM 中的所有元素重新渲染。
3、資源加載阻塞,比如js資源放在body之前、行內(nèi)script阻塞、css加載會(huì)阻塞DOM樹渲染(css并不會(huì)阻塞DOM樹的解析)資源過(guò)大阻塞
至于第二個(gè)問(wèn)題,詳見網(wǎng)紅問(wèn)題--前端性能優(yōu)化(全流程) 或者 js回流和重繪
現(xiàn)在看來(lái),這兩點(diǎn)大條件是客觀存在的,我們無(wú)法改變,所以這時(shí)候解決的方案就呼之欲出了,別讓瀏覽器一次性渲染這么多元素,這里通常會(huì)對(duì)應(yīng)三種做法來(lái)減少元素渲染。
數(shù)據(jù)分頁(yè)
這個(gè)方案是大家瀏覽到頁(yè)面所常用的,通常在需要展示非常多行的數(shù)據(jù)時(shí)頁(yè)面會(huì)采用分頁(yè)的做法來(lái)分割數(shù)據(jù),但在SQL結(jié)果集的場(chǎng)景下并不是通用方案,原因是雖然該方法減少了一次性所渲染的行數(shù),但是如果查詢的表列數(shù)非常多的話,還是有很大概率需要渲染非常多的元素,所以不是一個(gè)穩(wěn)妥的選型。
無(wú)限滾動(dòng)
該方案的解決方法是第一次只渲染所能承受范圍內(nèi)的數(shù)據(jù)量,當(dāng)滾動(dòng)條拖動(dòng)接近底部(或右部)時(shí),再去追加下一批所需要渲染的元素,該方案也是有一個(gè)明顯的缺陷在于,無(wú)限地滾動(dòng)下去必然會(huì)觸及瀏覽器的性能瓶頸,而且所需要渲染的元素會(huì)越來(lái)越多,性能遲早會(huì)被拖垮。
虛擬滾動(dòng)
其實(shí)答案已經(jīng)隱藏在上面兩種解決方案里面了,數(shù)據(jù)分頁(yè)的方案是一次性渲染固定行數(shù)和列數(shù)的數(shù)據(jù)量,缺點(diǎn)是怕一次性的量就逼近上限。無(wú)限滾動(dòng)的方案是想看更多數(shù)據(jù)的時(shí)候再繼續(xù)渲染,不看就不渲染避免性能浪費(fèi),但缺點(diǎn)就在于只要一直觸發(fā)“繼續(xù)看”的操作,那么之前遺留的數(shù)據(jù)將會(huì)越來(lái)越多導(dǎo)致性能雪崩。
這時(shí)候可以把兩個(gè)方案中和一下,既然在有限的視窗中我們只能看到一部分的數(shù)據(jù),那么我們就通過(guò)計(jì)算可視范圍內(nèi)的單元格,這樣就保證了每一次拖動(dòng),我們渲染的 DOM 元素始終是可控的,不會(huì)像數(shù)據(jù)分頁(yè)方案怕一次性渲染過(guò)多,也不會(huì)發(fā)生無(wú)限滾動(dòng)方案中的老數(shù)據(jù)堆積現(xiàn)象。接下來(lái)我們用一張圖來(lái)表示虛擬滾動(dòng)的表現(xiàn)形式。

根據(jù)圖中我們可以看到,無(wú)論我們?nèi)绾螡L動(dòng),我們可視區(qū)域的大小其實(shí)是不變的,那么要做到性能最大化就需要盡量少地渲染 DOM 元素,而這個(gè)最小值也就是可視范圍內(nèi)需要展示的內(nèi)容,也就是圖中的綠色區(qū)塊,在可視區(qū)域之外的元素均可以不做渲染。
可以通過(guò)如下幾步來(lái)實(shí)現(xiàn)虛擬滾動(dòng):
- 每一行的高度需要相同,方便計(jì)算
- 需要得知渲染的數(shù)據(jù)量(數(shù)組長(zhǎng)度),可基于總量和每個(gè)元素的高度計(jì)算出容器整體的所需高度,這樣就可以偽造一個(gè)真實(shí)的滾動(dòng)條
- 獲取可視區(qū)域的高度
- 在滾動(dòng)事件觸發(fā)后,滾動(dòng)條的距頂距離也可以理解為這個(gè)數(shù)據(jù)量中的偏移量,再根據(jù)可視區(qū)域本身的高度,算出本次偏移的截止量,這樣就得到了需要渲染的具體數(shù)據(jù)
- 如果類似于渲染一個(gè)寬表,單行可橫向拆分為多列,那么在X軸上同理實(shí)現(xiàn)一次,就可以橫向的虛擬滾動(dòng)