CSS 效能最佳化

在開發網站時,您需要考慮瀏覽器如何處理您網站上的 CSS。為了緩解 CSS 可能引起的任何效能問題,您應該對其進行最佳化。例如,您應該最佳化 CSS 以緩解渲染阻塞並儘量減少所需的重排次數。本文將引導您瞭解關鍵的 CSS 效能最佳化技術。

預備知識 已安裝基本軟體,並具備 客戶端 Web 技術 的基礎知識。
目標 瞭解 CSS 對網站效能的影響以及如何最佳化 CSS 以提高效能。

最佳化還是不最佳化

在開始最佳化 CSS 之前,您應該回答的第一個問題是“我需要最佳化什麼?”。下面討論的一些技巧和技術是良好的實踐,幾乎適用於任何 Web 專案,而另一些則僅在某些情況下需要。試圖將所有這些技術應用到所有地方可能是不必要的,並且可能會浪費您的時間。您應該弄清楚每個專案實際需要哪些效能最佳化。

為此,您需要測量您網站的效能。如上一個連結所示,有幾種不同的方法來測量效能,其中一些涉及複雜的效能 API。然而,最好的入門方法是學習如何使用內建瀏覽器網路效能工具等工具,檢視頁面載入的哪些部分花費了很長時間並需要最佳化。

最佳化渲染

瀏覽器遵循特定的渲染路徑——繪製只發生在佈局之後,而佈局發生在渲染樹建立之後,渲染樹反過來又需要 DOM 和 CSSOM 樹。

向用戶顯示未設定樣式的頁面,然後在解析 CSS 樣式後重新繪製,這將是一種糟糕的使用者體驗。因此,CSS 在瀏覽器確定需要 CSS 之前會阻塞渲染。瀏覽器在下載 CSS 並構建 CSS 物件模型 (CSSOM) 後才能繪製頁面。

為了最佳化 CSSOM 構建並提高頁面效能,您可以根據 CSS 的當前狀態執行以下一項或多項操作:

  • 刪除不必要的樣式:這聽起來很明顯,但令人驚訝的是,有多少開發人員忘記清理在開發過程中新增到樣式表中最終未使用的 CSS 規則。所有樣式都會被解析,無論它們是否在佈局和繪製期間使用,因此刪除未使用的樣式可以加快頁面渲染速度。正如 如何從網站刪除未使用的 CSS? (csstricks.com, 2019) 所總結的,對於大型程式碼庫來說,這是一個難以解決的問題,並且沒有可靠地查詢和刪除未使用的 CSS 的靈丹妙藥。您需要努力保持 CSS 模組化,並謹慎、有意識地新增和刪除內容。

  • 將 CSS 拆分為單獨的模組:保持 CSS 模組化意味著在頁面載入時不需要的 CSS 可以在以後載入,從而減少初始 CSS 渲染阻塞和載入時間。最簡單的方法是將 CSS 拆分為單獨的檔案,並僅載入所需的部分。

    html
    <!-- Loading and parsing styles.css is render-blocking -->
    <link rel="stylesheet" href="styles.css" />
    
    <!-- Loading and parsing print.css is not render-blocking -->
    <link rel="stylesheet" href="print.css" media="print" />
    
    <!-- Loading and parsing mobile.css is not render-blocking on large screens -->
    <link
      rel="stylesheet"
      href="mobile.css"
      media="screen and (width <= 480px)" />
    

    上面的示例提供了三組樣式——總是會載入的預設樣式、只有在列印文件時才會載入的樣式,以及只有窄屏裝置才會載入的樣式。預設情況下,瀏覽器假定每個指定的樣式表都會阻塞渲染。您可以透過新增包含媒體查詢media屬性來告訴瀏覽器何時應該應用樣式表。當瀏覽器看到它只需要在特定場景中應用的樣式表時,它仍然會下載該樣式表,但不會阻塞渲染。透過將 CSS 拆分成多個檔案,主渲染阻塞檔案(在本例中為styles.css)會小得多,從而減少了渲染被阻塞的時間。

  • 壓縮和精簡您的 CSS:精簡是指在程式碼投入生產後,刪除檔案中所有僅用於提高人類可讀性的空白。透過精簡 CSS,您可以顯著縮短載入時間。精簡通常作為構建過程的一部分完成(例如,大多數 JavaScript 框架在您構建準備部署的專案時都會精簡程式碼)。除了精簡之外,請確保您的網站託管伺服器在提供檔案之前對其使用 gzip 等壓縮技術。

  • 簡化選擇器:人們經常編寫比應用所需樣式更復雜的選擇器。這不僅增加了檔案大小,也增加了這些選擇器的解析時間。例如

    css
    /* Very specific selector */
    body div#main-content article.post h2.headline {
      font-size: 24px;
    }
    
    /* You probably only need this */
    .headline {
      font-size: 24px;
    }
    

    讓你的選擇器不那麼複雜和具體也利於維護。簡單的選擇器很容易理解它們在做什麼,而且如果選擇器不那麼具體,以後需要時也很容易覆蓋樣式。

  • 不要對超出需要範圍的元素應用樣式:一個常見的錯誤是使用通用選擇器,或者至少是對超出需要範圍的更多元素應用樣式。這種樣式可能會對效能產生負面影響,尤其是在大型網站上。

    css
    /* Selects every element inside the <body> */
    body * {
      font-size: 14px;
      display: flex;
    }
    

    請記住,許多屬性(例如font-size)會從其父級繼承值,因此您不需要在所有地方都應用它們。而像Flexbox這樣強大的工具需要謹慎使用。在所有地方使用它們可能會導致各種意想不到的行為。

  • 使用 CSS Sprites 減少圖片 HTTP 請求CSS Sprites 是一種技術,它將您想要在網站上使用的多個小圖片(例如圖示)放入一個圖片檔案中,然後使用不同的 background-position 值來顯示您想要在每個不同位置顯示的圖片塊。這可以顯著減少獲取圖片所需的 HTTP 請求數量。

  • 預載入重要資源:您可以使用 rel="preload"<link> 元素轉換為關鍵資源的預載入器。這包括 CSS 檔案、字型和圖片。

    html
    <link rel="preload" href="style.css" as="style" />
    
    <link
      rel="preload"
      href="ComicSans.woff2"
      as="font"
      type="font/woff2"
      crossorigin />
    
    <link
      rel="preload"
      href="bg-image-wide.png"
      as="image"
      media="(width > 600px)" />
    

    使用 preload,瀏覽器會盡快獲取引用的資源,並將它們儲存在瀏覽器快取中,以便在後續程式碼中引用它們時能更快地使用。預載入使用者在頁面早期會遇到的高優先順序資源非常有用,這樣可以使體驗儘可能流暢。請注意,您還可以使用 media 屬性來建立響應式預載入器。

    另請參閱 web.dev 上的預載入關鍵資產以提高載入速度 (2020)。

處理動畫

動畫可以提高感知效能,使介面感覺更靈敏,並讓使用者在等待頁面載入時(例如載入旋轉器)感受到進度。然而,更大的動畫和更多的動畫自然需要更多的處理能力來處理,這可能會降低效能。

最簡單的建議是減少所有不必要的動畫。您還可以為使用者提供一個控制元件/網站偏好設定,如果他們使用低功耗裝置或電池電量有限的移動裝置,則可以關閉動畫。您還可以使用 JavaScript 來控制是否首先將動畫應用於頁面。還有一個名為prefers-reduced-motion的媒體查詢,可以根據使用者的作業系統級動畫偏好來有選擇地提供動畫樣式。

對於必要的 DOM 動畫,建議儘可能使用CSS 動畫,而不是 JavaScript 動畫(Web Animations API 提供了一種使用 JavaScript 直接與 CSS 動畫掛鉤的方法)。

選擇要動畫的屬性

接下來,動畫效能在很大程度上取決於您正在動畫的屬性。某些屬性在動畫時會觸發重排(因此也會觸發重繪),應避免使用。這些屬性包括:

現代瀏覽器足夠智慧,只會重繪文件中更改的區域,而不是整個頁面。因此,更大的動畫成本更高。

如果可能的話,最好對不會引起重排/重繪的屬性進行動畫。這包括:

在 GPU 上動畫

為了進一步提高效能,您應該考慮將動畫工作從主執行緒轉移到裝置的 GPU(也稱為合成)。這透過選擇瀏覽器會自動傳送到 GPU 處理的特定動畫型別來完成;這些包括

在 GPU 上進行動畫可以提高效能,尤其是在移動裝置上。然而,將動畫轉移到 GPU 並非總是那麼簡單。請閱讀 CSS GPU 動畫:正確地做 (smashingmagazine.com, 2016) 以獲取非常有用的詳細分析。

使用 will-change 最佳化元素更改

瀏覽器可能會在元素實際更改之前設定最佳化。這些最佳化可以透過在需要之前完成可能開銷較大的工作來提高頁面響應性。CSS will-change 屬性向瀏覽器提示元素預期會如何更改。

注意:will-change 旨在作為最後手段來處理現有效能問題。它不應用於預測效能問題。

css
.element {
  will-change: opacity, transform;
}

最佳化渲染阻塞

CSS 可以使用媒體查詢將樣式限定在特定條件下。媒體查詢對於響應式網頁設計很重要,有助於我們最佳化關鍵渲染路徑。瀏覽器會阻塞渲染,直到解析所有這些樣式,但不會阻塞它知道不會使用的樣式(例如列印樣式表)的渲染。透過根據媒體查詢將 CSS 拆分為多個檔案,您可以防止在下載未使用的 CSS 時阻塞渲染。要建立非阻塞 CSS 連結,請將不立即使用的樣式(例如列印樣式)移動到單獨的檔案中,在 HTML 標記中新增一個 <link>,並新增一個媒體查詢,在這種情況下,它宣告這是一個列印樣式表。

html
<!-- Loading and parsing styles.css is render-blocking -->
<link rel="stylesheet" href="styles.css" />

<!-- Loading and parsing print.css is not render-blocking -->
<link rel="stylesheet" href="print.css" media="print" />

<!-- Loading and parsing mobile.css is not render-blocking on large screens -->
<link rel="stylesheet" href="mobile.css" media="screen and (width <= 480px)" />

預設情況下,瀏覽器假定每個指定的樣式表都會阻塞渲染。透過新增帶有媒體查詢media屬性,告訴瀏覽器何時應該應用樣式表。當瀏覽器看到它知道只需在特定場景中應用的樣式表時,它仍然會下載該樣式表,但不會阻塞渲染。透過將 CSS 拆分為多個檔案,主渲染阻塞檔案(在本例中為styles.css)會小得多,從而減少了渲染被阻塞的時間。

提升字型效能

本節包含一些提高網頁字型效能的有用提示。

總的來說,請仔細考慮您網站上使用的字型。有些字型檔案可能非常大(幾兆位元組)。雖然使用大量字型來增加視覺吸引力可能很誘人,但這會顯著減慢頁面載入速度,並導致您的網站看起來一團糟。您可能只需要大約兩到三種字型,如果您選擇使用網頁安全字型,您甚至可以減少更多。

字型載入

請記住,字型只在實際使用 font-family 屬性應用於元素時才載入,而不是在首次使用 @font-face at-rule 引用時載入。

css
/* Font not loaded here */
@font-face {
  font-family: "Open Sans";
  src: url("OpenSans-Regular-webfont.woff2") format("woff2");
}

h1,
h2,
h3 {
  /* It is actually loaded here */
  font-family: "Open Sans", sans-serif;
}

因此,使用 rel="preload" 儘早載入重要字型可能是有益的,這樣它們在實際需要時會更快可用。

html
<link
  rel="preload"
  href="OpenSans-Regular-webfont.woff2"
  as="font"
  type="font/woff2"
  crossorigin />

如果你的 font-family 宣告隱藏在一個大型外部樣式表中,並且在解析過程的後期才能被訪問,那麼這更有可能帶來好處。然而,這是一個權衡——字型檔案相當大,如果你預載入太多,可能會延遲其他資源。

您還可以考慮:

只加載您需要的字形

在為主體文字選擇字型時,很難確定其中將使用的字形,尤其是當您處理使用者生成內容和/或多種語言的內容時。

然而,如果您知道將使用一組特定的字形(例如,僅用於標題或特定標點符號的字形),您可以限制瀏覽器必須下載的字形數量。這可以透過建立僅包含所需子集的字型檔案來完成。這個過程稱為子集化unicode-range @font-face 描述符可以用於指定何時使用您的子集字型。如果頁面不使用此範圍內的任何字元,則不下載該字型。

css
@font-face {
  font-family: "Open Sans";
  src: url("OpenSans-Regular-webfont.woff2") format("woff2");
  unicode-range: U+0025-00FF;
}

使用 font-display 描述符定義字型顯示行為

應用於 @font-face 規則的 font-display 描述符定義了瀏覽器如何載入和顯示字型檔案,允許在字型載入或載入失敗時,使用備用字型顯示文字。這透過使文字可見而不是顯示空白螢幕來提高效能,但代價是會出現未樣式化文字的閃爍。

css
@font-face {
  font-family: "someFont";
  src: url("/path/to/fonts/someFont.woff") format("woff");
  font-weight: normal;
  font-style: normal;
  font-display: fallback;
}

使用 CSS 包含最佳化樣式重新計算

透過使用CSS 包含模組中定義的屬性,您可以指示瀏覽器隔離頁面的不同部分並獨立最佳化它們的渲染。這可以提高各個部分的渲染效能。例如,您可以指定瀏覽器在某些容器在視口中可見之前不渲染它們。

contain 屬性允許作者精確指定他們希望應用於頁面上各個容器的包含型別。這允許瀏覽器對 DOM 的有限部分重新計算佈局、樣式、繪製、大小或它們的任意組合。

css
article {
  contain: content;
}

content-visibility 屬性是一個有用的快捷方式,它允許作者在容器集合上應用一組強大的包含限制,並指定瀏覽器在需要時才對這些容器進行佈局和渲染。

第二個屬性 contain-intrinsic-size 也可用,它允許您為處於包含效果下的容器提供一個佔位符大小。這意味著即使容器的內容尚未渲染,它們也會佔用空間,從而允許包含在不冒捲軸移動和元素渲染並進入檢視時卡頓的風險下發揮其效能魔力。這提高了內容載入時使用者體驗的質量。

css
article {
  content-visibility: auto;
  contain-intrinsic-size: 1000px;
}

最佳化 :has() 選擇器

:has() 偽類提供了強大的選擇功能,但需要謹慎使用以避免效能瓶頸。有關編寫高效 :has() 選擇器的詳細指南,請參閱 :has() 參考文件中的效能注意事項

另見