降低樣式計算的範圍和複雜度

JavaScript 通常是視覺變化的觸發因素。有時候,光是靠樣式操控就能直接進行,有時則是在計算結果中會改變視覺上的變化,例如搜尋或排序某些資料。執行時間不佳或長時間執行的 JavaScript 是效能問題的常見原因,請盡可能降低這類問題的影響。

變更 DOM 時,包括新增及移除元素、變更屬性、類別或動畫,都會造成瀏覽器重新計算元素樣式,而且在許多情況下,頁面的版面配置 (或重排) 都會導致頁面的版面配置 (或重排)。這項程序稱為「運算樣式計算」

運算樣式的第一部分,是建立一組相符的選取器,基本上可讓瀏覽器確認任何指定元素適用的類別、虛擬選取器和 ID。

程序的第二部分,涵蓋了比對選取器中的所有樣式規則,並確定元素有哪些最終樣式。

摘要

  • 降低樣式計算成本有助於縮短互動延遲時間。
  • 降低選取器的複雜性;使用以類別為中心的方法 (例如 BEM)。
  • 減少需要計算樣式的元素數量。

樣式重新計算的時間和互動延遲時間

「與下一個顯示的內容互動」 (INP) 是一項以使用者為中心的執行階段成效指標,可用於評估網頁對使用者輸入內容的整體回應速度。這項指標評估互動延遲時間時,會從使用者與網頁互動起算,直到瀏覽器繪製下一個頁框 (顯示使用者介面中相應的視覺更新) 為止。

互動過程中的重要組成部分,是指繪製下一個影格所需的時間。系統為了呈現下一個畫面而完成的轉譯工作由許多部分組成,包括計算版面配置、繪製及合成工作之前的網頁樣式。雖然本文僅著重說明樣式計算費用,但要強調的是,減少轉譯階段中原本要互動的任何部分將可縮短總延遲時間 (包括樣式計算)。

降低選取器的複雜度

在最簡單的情況下,您可以使用類別來參照 CSS 中的元素:

.title {
  /* styles */
}

但隨著任何專案成長,CSS 程式碼可能會變得更加複雜,因此最終產生的選取器可能會像這樣:

.box:nth-last-child(-n+1) .title {
  /* styles */
}

要瞭解這些樣式如何套用至網頁,瀏覽器必須有效詢問「這是一個包含 title 的元素,其父項的父項會是第 n 個,再加上 1 個類別為 box 的元素」嗎?視所使用的選取器和瀏覽器而定,要解決這個問題「可能會」需要大量時間。可以將選取器的預期行為改為類別:

.final-box-title {
  /* styles */
}

您可以採用類別名稱來解決問題,但瀏覽器此時的工作會變得更簡單。舉例來說,在舊版中,為了瞭解該元素是其類型的最後一項,瀏覽器必須先知道所有其他元素的所有資訊,以及之後包含的任何元素是否為 nth-last-child。比起單純將選取器與元素比對,因為其類別相符,所以成本可能較高。

減少設定樣式的元素數量

另一個效能考量因素,通常是許多樣式更新越重要,也就是在元素變更時必須執行的工作量。

通常來說,計算元素樣式時最差的成本,就是元素數量乘以選取器數,因為每個元素都需要至少針對每種樣式檢查一次,才能確認其是否相符。

樣式計算通常可以直接指定幾個元素,而不是使網頁整體失效。在新式瀏覽器中,這個問題通常較少發生,因為瀏覽器不一定需要檢查所有可能受變更影響的元素。而版本較舊的瀏覽器,不一定能針對這類工作進行最佳化。盡可能減少無效元素的數量

測量樣式重新計算的費用

測量樣式重新計算費用的方法之一,就是使用 Chrome 開發人員工具的效能面板。首先,請開啟開發人員工具,前往標示為「效能」的分頁,按下記錄,然後與頁面互動。停止錄製後,你會看到下圖:

顯示樣式計算的開發人員工具。

頂端的長條是小型火焰圖,也會每秒繪製影格。活動越接近條紋底部,瀏覽器就會繪製較快的影格。如果您發現火焰圖在頂端,且上方有紅色條紋,表示您有某些工作導致影格運作過久。

在 Chrome 開發人員工具中已填入效能面板的活動摘要中,放大 Chrome 開發人員工具的活動摘要。

如果在互動 (例如捲動) 過程中有長時間運作的影格,我們會進一步檢查。如果紫色區塊較大,請放大活動並選取任何標示為「重新計算樣式」的作品,進一步瞭解可能昂貴的樣式重新計算工作。

取得長時間執行樣式計算的詳細資訊,包括受到樣式重新計算工作影響的元素數量等重要資訊。

這個片段中有需要花費超過 25 毫秒的長時間重新計算工作。

如果點選事件本身,就會得到呼叫堆疊。如果算繪作業是由使用者互動所導致,系統會呼叫 JavaScript 中負責觸發樣式變更的位置。此外,您也可以取得受此變更影響的元素數量 (在本例中為超過 900 個元素),以及執行樣式計算工作所花的時間。你可以根據這項資訊嘗試找出程式碼修正方式。

使用 Block、Element、Modifier

針對 BEM (Block、Element、Modifier) 這類編碼的方法,實際上是在符合上述效能的選取器中烘焙,因為它會建議所有項目都有一個類別,而且您需要在您需要的階層中,這些程式碼也會內建到類別名稱中:

.list {
  /* Styles */
}

.list__list-item {
  /* Styles */
}

如果您需要一些修飾符 (如上文所示,我們要為最後一個子項進行特殊的修飾符),請按照以下方式新增:

.list__list-item--last-child {
  /* Styles */
}

如果您正在尋找整理 CSS 的好方法,從結構的角度來看,BEM 是很好的起點,但也因為樣式查詢的簡化而擴大。

如果不喜歡 BEM,還有其他方法可以處理 CSS,但應在做法人體工學的情況下,一併評估成效考量。

資源

Markus Spiske 撰寫的「Unsplash」主頁橫幅。