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