大型DOM結(jié)構(gòu)是如何影響交互性的
沒(méi)有辦法繞過(guò)這一點(diǎn):當(dāng)你構(gòu)建一個(gè)網(wǎng)頁(yè)時(shí),該頁(yè)面一定會(huì)有一個(gè)文檔對(duì)象模型(DOM)。DOM代表了你頁(yè)面HTML的結(jié)構(gòu),并為JavaScript和CSS提供了訪問(wèn)頁(yè)面結(jié)構(gòu)和內(nèi)容的途徑。
然而,問(wèn)題在于DOM的大小會(huì)影響瀏覽器快速和高效地渲染頁(yè)面的能力。一般來(lái)說(shuō),DOM越大,最初渲染該頁(yè)面以及稍后在頁(yè)面生命周期中更新其渲染就越昂貴。
這在具有非常大的DOM的頁(yè)面上會(huì)變得問(wèn)題重重,因?yàn)樾薷幕蚋翫OM的交互會(huì)觸發(fā)昂貴的布局工作,從而影響頁(yè)面快速響應(yīng)的能力。昂貴的布局工作可能會(huì)影響頁(yè)面從交互到下一次繪制(INP)的速度;如果你希望頁(yè)面能快速響應(yīng)用戶交互,確保你的DOM大小只有必要的大小是很重要的。
什么時(shí)候頁(yè)面的DOM過(guò)大?
了解DOM元素和DOM節(jié)點(diǎn)之間的區(qū)別非常重要。DOM元素是指DOM樹(shù)中的一個(gè)特定HTML元素。DOM節(jié)點(diǎn)與DOM元素有重疊的含義,但其定義擴(kuò)展到包括注釋、空白和文本。雖然Lighthouse的DOM大小審計(jì)是指DOM節(jié)點(diǎn),但本指南將盡可能地提到DOM元素而不是節(jié)點(diǎn)。
根據(jù) Lighthouse,當(dāng)頁(yè)面的DOM大小超過(guò)1400個(gè)節(jié)點(diǎn)時(shí),就過(guò)大了。當(dāng)頁(yè)面的DOM超過(guò) 800個(gè)節(jié)點(diǎn)時(shí),Lighthouse 將開(kāi)始發(fā)出警告。以以下HTML為例:
<ul>
<li>List item one.</li>
<li>List item two.</li>
<li>List item three.</li>
</ul>
在上面的代碼中,有四個(gè)DOM元素:<ul> 元素及其三個(gè) <li> 子元素。你的網(wǎng)頁(yè)幾乎肯定會(huì)有比這更多的節(jié)點(diǎn),因此了解你可以如何控制DOM大小是很重要的——以及一旦你讓頁(yè)面的DOM盡可能小,其他優(yōu)化渲染工作的策略。
大型DOM如何影響頁(yè)面性能?
大型 DOM以幾種方式影響頁(yè)面性能:
- 在頁(yè)面的初始渲染期間。當(dāng) CSS 應(yīng)用于頁(yè)面時(shí),會(huì)創(chuàng)建一個(gè)類似于 DOM 的結(jié)構(gòu),稱為 CSS 對(duì)象模型(CSSOM)。隨著CSS選擇器特異性的增加,CSSOM變得更復(fù)雜,需要更多的時(shí)間來(lái)完成繪制網(wǎng)頁(yè)所需的布局、樣式、合成和繪制工作。這增加了頁(yè)面加載初期交互的延遲。
- 當(dāng)交互修改DOM時(shí),無(wú)論是通過(guò)元素的插入或刪除,還是通過(guò)修改DOM內(nèi)容和樣式,渲染該更新所需的工作可能會(huì)導(dǎo)致非常昂貴的布局、樣式、合成和繪制工作。與頁(yè)面的初始渲染一樣,CSS選擇器特異性的增加會(huì)增加交互導(dǎo)致的HTML元素插入到DOM時(shí)的渲染工作。
- 當(dāng) JavaScript 查詢DOM時(shí),對(duì) DOM 元素的引用存儲(chǔ)在內(nèi)存中。例如,如果你調(diào)用 document.querySelectorAll 來(lái)選擇頁(yè)面上的所有<div> 元素,如果結(jié)果返回大量的DOM元素,內(nèi)存成本可能會(huì)相當(dāng)可觀。
所有這些都會(huì)影響交互性,但上面列表中的第二項(xiàng)尤為重要。如果一個(gè)交互導(dǎo)致DOM的改變,它可能觸發(fā)大量的工作,從而導(dǎo)致頁(yè)面上不良的交互到下一次繪制(INP)。
如何測(cè)量DOM大???
可以用幾種方式來(lái)測(cè)量DOM大小。第一種方法是使用Lighthouse。當(dāng)你運(yùn)行一個(gè)審計(jì)時(shí),當(dāng)前頁(yè)面的DOM統(tǒng)計(jì)信息將出現(xiàn)在"Diagnostics"標(biāo)題下的"Avoid an excessive DOM size"審計(jì)部分。在這一部分中,你可以看到DOM元素的總數(shù)、包含最多子元素的DOM元素,以及最深的DOM元素。
更簡(jiǎn)單的方法是在任何主要瀏覽器的開(kāi)發(fā)者工具中使用JavaScript控制臺(tái)。要獲取DOM中HTML元素的總數(shù),你可以在頁(yè)面加載后在控制臺(tái)中使用以下代碼:
document.querySelectorAll('*').length;
請(qǐng)注意,上面的代碼片段僅包括DOM中HTML元素的數(shù)量。它不包括DOM中的所有節(jié)點(diǎn)。
如果你想實(shí)時(shí)查看DOM大小的更新,你也可以使用性能監(jiān)視工具。使用這個(gè)工具,你可以將布局和樣式操作(以及其他性能方面)與當(dāng)前的DOM大小進(jìn)行關(guān)聯(lián)。
果DOM的大小接近Lighthouse DOM大小的警告閾值,或者完全不合格,下一步就是找出如何減小DOM的大小,以提高你的頁(yè)面對(duì)用戶交互的響應(yīng)能力,從而改善你網(wǎng)站的交互到下一次繪制(INP)。
如何測(cè)量受交互影響的DOM元素?cái)?shù)量?
如果你在實(shí)驗(yàn)室中分析一個(gè)你懷疑與頁(yè)面DOM大小有關(guān)的慢速交互,你可以通過(guò)選擇標(biāo)有“重新計(jì)算樣式”的性能分析器中的任何活動(dòng),并觀察底部面板中的上下文數(shù)據(jù)來(lái)了解有多少DOM元素受到了影響。
在上面的截圖中,注意到當(dāng)選中時(shí),樣式重新計(jì)算的工作顯示了受影響元素的數(shù)量。雖然上面的截圖顯示了一個(gè)具有多個(gè)DOM元素的頁(yè)面上DOM大小對(duì)渲染工作影響的極端案例,但這種診斷信息在任何情況下都是有用的,以確定DOM的大小是否是響應(yīng)交互到下一幀繪制所需時(shí)間的限制因素。
如何減小DOM大小?
除了審查你網(wǎng)站的HTML以刪除不必要的標(biāo)記外,減小DOM大小的主要方法是減小DOM深度。如果你在瀏覽器開(kāi)發(fā)者工具的“Elements”選項(xiàng)卡中看到像這樣的標(biāo)記,那么你的DOM可能不必要地過(guò)深:
<div>
<div>
<div>
<div>
<!-- Contents -->
</div>
</div>
</div>
</div>
當(dāng)你看到這樣的模式時(shí),你可能可以通過(guò)扁平化你的DOM結(jié)構(gòu)來(lái)簡(jiǎn)化它們。這樣做將減少DOM元素的數(shù)量,并可能給你一個(gè)機(jī)會(huì)來(lái)簡(jiǎn)化頁(yè)面樣式。
DOM深度也可能是你使用的框架的一個(gè)癥狀。特別是,基于組件的框架(如依賴于JSX的那些)要求你在父容器中嵌套多個(gè)組件。
然而,許多框架允許你通過(guò)使用所謂的片段(fragments)來(lái)避免嵌套組件。提供片段功能的基于組件的框架包括但不限于以下幾種:
- React
- Preact
- Vue
- Svelte
通過(guò)在你選擇的框架中使用片段,你可以減小DOM深度。如果你擔(dān)心扁平化DOM結(jié)構(gòu)對(duì)樣式有影響,你可能會(huì)從使用更現(xiàn)代(和更快)的布局模式(如flexbox或grid)中受益。
考慮其他策略
即使你努力扁平化你的DOM樹(shù)并移除不必要的HTML元素以保持你的DOM盡可能小,它仍然可能相當(dāng)大,并且在響應(yīng)用戶交互時(shí)觸發(fā)大量的渲染工作。如果你發(fā)現(xiàn)自己處于這種情況,有一些其他策略你可以考慮以限制渲染工作。
考慮一種增量方法
你可能處于這樣一個(gè)位置,即頁(yè)面的大部分在首次渲染時(shí)對(duì)用戶來(lái)說(shuō)并不可見(jiàn)。這可能是通過(guò)在啟動(dòng)時(shí)省略DOM的那些部分來(lái)懶加載HTML的一個(gè)機(jī)會(huì),但在用戶與需要最初隱藏的頁(yè)面部分進(jìn)行交互時(shí)再將它們添加進(jìn)去。
限制CSS選擇器的復(fù)雜性
當(dāng)瀏覽器解析你的CSS中的選擇器時(shí),它必須遍歷DOM樹(shù)以了解這些選擇器是如何(以及是否)應(yīng)用于當(dāng)前布局的。這些選擇器越復(fù)雜,瀏覽器就需要做更多的工作,以便進(jìn)行頁(yè)面的初始渲染,以及如果頁(yè)面因交互而發(fā)生變化時(shí)增加樣式重新計(jì)算和布局工作。
使用 content-visibility 屬性
CSS提供了 content-visibility 屬性,這實(shí)際上是一種懶加載屏幕外DOM元素的方法。當(dāng)這些元素接近視口時(shí),它們會(huì)根據(jù)需要進(jìn)行渲染。content-visibility 的好處不僅在于大幅減少了初始頁(yè)面渲染時(shí)的渲染工作量,而且在頁(yè)面DOM因用戶交互而改變時(shí),也會(huì)跳過(guò)屏幕外元素的渲染工作。
結(jié)論
將你的DOM大小減少到只有嚴(yán)格必需的部分是優(yōu)化網(wǎng)站INP(Interaction to Next Paint,交互到下一次繪制)的一個(gè)好方法。通過(guò)這樣做,你可以減少瀏覽器在DOM更新時(shí)進(jìn)行布局和渲染工作所需的時(shí)間。即使你不能有意義地減小DOM大小,也有一些技術(shù)你可以用來(lái)將渲染工作隔離到一個(gè)DOM子樹(shù),例如CSS containment和 content-visibility CSS屬性。
無(wú)論你如何去做,創(chuàng)造一個(gè)最小化渲染工作的環(huán)境,以及減少頁(yè)面響應(yīng)交互時(shí)所做的渲染工作,結(jié)果將是你的網(wǎng)站在用戶與其交互時(shí)會(huì)感覺(jué)更加響應(yīng)靈敏。這意味著你的網(wǎng)站將具有更低的INP,從而轉(zhuǎn)化為更好的用戶體驗(yàn)。