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 (max-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 雪碧減少影像 HTTP 請求CSS 雪碧是一種技術,它將您想在網站上使用的幾個小影像(例如圖示)放到一個影像檔案中,然後使用不同的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="(min-width: 601px)" />
    
    使用preload,瀏覽器會盡快獲取引用的資源並將其放入瀏覽器快取中,以便在後續程式碼中引用它們時可以更快地使用它們。預載入使用者在頁面早期遇到的高優先順序資源很有用,這樣可以使體驗儘可能流暢。請注意,您還可以使用media屬性建立響應式預載入器。另請參閱 web.dev 上的預載入關鍵資源以提高載入速度(2020)

處理動畫

動畫可以提高感知效能,使介面感覺更靈敏,並使使用者在等待頁面載入時感覺正在取得進展(例如載入微調器)。但是,較大的動畫和較多的動畫自然需要更多的處理能力來處理,這會降低效能。

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

對於必要的 DOM 動畫,建議儘可能使用CSS 動畫,而不是 JavaScript 動畫(Web 動畫 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 (max-width: 480px)" />

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

提升字型效能

本節包含一些改進網路字型效能的有用技巧。

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

字型載入

請記住,僅當使用font-family 屬性實際將字型應用於元素時才會載入字型,而不是在首次使用@font-face at-規則引用它時。

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";
}

因此,使用 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 at-規則,font-display 描述符定義瀏覽器如何載入和顯示字型檔案,允許文字在字型載入或載入失敗時顯示回退字型。這透過使文字可見(而不是出現空白螢幕)來提高效能,但代價是可能會出現未設定樣式文字的閃爍。

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

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

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

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

css
article {
  contain: content;
}

content-visibility 屬性是一個有用的快捷方式,它允許作者在一組容器上應用一組強大的包含,並指定瀏覽器在需要之前不應佈局和渲染這些容器。

還有一個名為contain-intrinsic-size的第二個屬性,它允許您在容器受到包含影響時為其提供一個佔位符大小。這意味著即使容器的內容尚未渲染,容器也會佔用空間,從而允許包含發揮其效能優勢,而不會出現元素渲染並進入視野時捲軸偏移和卡頓的風險。這提高了使用者體驗的質量,因為內容已載入。

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

另請參閱