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 開發人員工具的效能面板。首先,請開啟開發人員工具,前往標示為「效能」的分頁,按下記錄,然後與頁面互動。停止錄製後,你會看到下圖:
頂端的長條是小型火焰圖,也會每秒繪製影格。活動越接近條紋底部,瀏覽器就會繪製較快的影格。如果您發現火焰圖在頂端,且上方有紅色條紋,表示您有某些工作導致影格運作過久。
如果在互動 (例如捲動) 過程中有長時間運作的影格,我們會進一步檢查。如果紫色區塊較大,請放大活動並選取任何標示為「重新計算樣式」的作品,進一步瞭解可能昂貴的樣式重新計算工作。
這個片段中有需要花費超過 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」主頁橫幅。