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

JavaScript 通常是視覺變化的觸發條件。有時這類變更會透過樣式操縱直接進行變更,有時可透過計算而產生視覺變化,例如搜尋或排序資料。造成時間不佳或長時間執行的 JavaScript 可能是效能問題的常見原因,您應盡可能降低這類問題的影響。

樣式計算

透過新增及移除元素、變更屬性、類別或播放動畫來變更 DOM 會導致瀏覽器重新計算元素樣式,在許多情況下,也會反映部分或全部頁面的版面配置。這項程序稱為「運算樣式計算」

瀏覽器會建立一組比對選取器開始計算樣式,以決定要將哪些類別、虛擬選取器和 ID 套用至任何指定元素。接著,系統會處理相符選取器中的樣式規則,然後判斷元素擁有的最終樣式。

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

與下一個顯示的內容互動 (INP) 是以使用者為中心的執行階段效能指標,可評估頁面對使用者輸入內容的整體回應效能。計算互動延遲時間,從使用者與網頁互動,到瀏覽器繪製下一個影格 (顯示使用者介面對應的視覺更新) 為止,然後再計算一次互動延遲時間。

一個互動的重要組成部分,就是繪製下一個影格所需的時間。為顯示下一個畫面而完成的轉譯工作由許多部分組成,包括計算版面配置、繪製和合成工作之前的頁面樣式。本頁面著重於樣式計算費用,但減少與互動相關的轉譯階段任何部分也會降低總延遲時間,包括樣式計算。

降低選取器的複雜度

簡化選取器名稱有助於加快網頁的樣式計算速度。最簡單的選取器只會參照類別名稱來參照 CSS 中的元素:

.title {
  /* styles */
}

但隨著任何專案成長,可能都需要更複雜的 CSS,因此最終產生的選取器可能會像這樣:

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

如要判斷這些樣式如何套用至網頁,瀏覽器必須有效詢問「這是一個包含 title 的類別,且其父項為 box 的減號子元素嗎?」。視所使用的選取器和相關瀏覽器而定,要調查可能需要很長的時間。為簡化這項作業,您可以將選取器變更為類別名稱:

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

這些替換類別名稱看起來可能不太理想,但可讓瀏覽器的工作變得更簡單。舉例來說,在舊版本中,假設瀏覽器知道元素是其類型的最後一項,則必須先瞭解所有其他元素的所有資訊,才能判斷之後是否有任何元素可能是 nth-last-child。比起將選取器與元素進行比對,這在運算上成本可能會高得多,因為其類別相符。

減少設定樣式的元素數量

另一個效能考量因素 (通常比選取器複雜性更重要),就是元素變更時需要執行的工作量。

一般而言,最差的計算費用就是元素數量乘以選取器數,因為瀏覽器需要針對每個元素至少檢查每個元素一次,才能確認是否相符。

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

測量樣式重新計算的費用

測量樣式重新計算費用的其中一種方法是使用 Chrome 開發人員工具的效能面板。如要開始使用,請執行下列操作:

  1. 開啟開發人員工具。
  2. 前往「成效」分頁。
  3. 按一下「錄製」
  4. 與頁面互動。

停止錄製後,你會看到如下圖所示的畫面:

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

頂端的長條是迷你火焰圖,也會每秒繪製影格。活動越接近條紋底部,瀏覽器繪製的影格速度就越快。如果您發現火焰圖朝向水平,且上方有紅色長條,表示您有導致影格長時間執行的工作。

在 Chrome 開發人員工具中已填入效能面板的活動摘要中,放大 Chrome 開發人員工具中的問題區域。
開發人員工具活動摘要中的長時間執行影格。

在互動 (例如捲動) 期間,長時間執行的影格值得更仔細查看。如果您看到大型紫色區塊,請放大該活動並選取任何標示為「Recallate Style」(重新計算樣式) 的工作,即可進一步瞭解可能昂貴的樣式重新計算工作。

取得長時間執行樣式計算的詳細資料,包括受到樣式重新計算工作影響的元素數量等重要資訊。
在開發人員工具摘要中,經過長時間執行樣式重新計算的結果只需超過 25 毫秒。

按一下活動就會顯示呼叫堆疊。如果算繪工作是由使用者互動造成,就會呼叫觸發樣式變更的 JavaScript。也會顯示變更所影響的元素數量 (在本例中為超過 900 個元素),以及樣式計算所花費的時間。您可以使用這項資訊嘗試在程式碼中尋找修正方法。

使用 Block、Element、Modifier

在符合效能優勢的選取器中,進行以下程式設計的方法:BEM (Block, Element, Modifier)。BEM 建議所有項目都有單一類別,在您需要階層的情況下,該階層也會納入類別名稱中:

.list {
  /* Styles */
}

.list__list-item {
  /* Styles */
}

如果您需要修飾符 (例如最後一個子項範例),可以按照以下方式新增修飾符:

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

BEM 是整理 CSS 的理想起點,從結構的角度來看,且由於樣式查詢簡化了其宣傳方式。

如果您不喜歡 BEM,還有其他方法可以處理 CSS,但建議您在開始前先評估 CSS 供應商的效能和人體工學。

資源

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