HTML 效能最佳化

HTML 預設情況下是快速且可訪問的。作為開發人員,我們的職責是確保在建立或編輯 HTML 程式碼時保留這兩個屬性。例如,當 <video> 嵌入的檔案大小過大,或者當 JavaScript 解析阻止關鍵頁面元素的渲染時,可能會出現問題。本文將引導您瞭解關鍵的 HTML 效能功能,這些功能可以顯著提高網頁的質量。

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

最佳化還是不最佳化

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

為此,您需要測量您網站的效能。正如該連結所示,有幾種不同的方法可以測量效能,其中一些涉及複雜的效能 API。然而,最好的入門方法是學習如何使用內建瀏覽器網路效能工具,來檢查頁面中載入時間過長且需要最佳化的部分。

關鍵 HTML 效能問題

HTML 在效能方面是簡單的——它主要是文字,體積小,因此大多下載和渲染速度快。可能影響網頁效能的關鍵問題包括:

  • 圖片和影片檔案的大小:考慮如何處理可替換元素(如 <img><video>)的內容非常重要。圖片和影片檔案通常很大,會顯著增加頁面的權重。因此,儘量減少使用者裝置下載的位元組數(例如,為移動裝置提供更小的圖片)非常重要。您還需要考慮透過僅在需要時才載入頁面上的圖片和影片來提高感知效能。
  • 嵌入內容的交付:這通常是嵌入在 <iframe> 元素中的內容。將內容載入到 <iframe> 中會顯著影響效能,因此應仔細考慮。
  • 資源載入順序:為了最大化感知和實際效能,HTML 應該首先按照其在頁面中出現的順序載入。然後,您可以使用各種功能來影響資源載入順序,以獲得更好的效能。例如,您可以儘早預載入關鍵的 CSS 和字型,但將非關鍵的 JavaScript 推遲到稍後。

注意: 有人主張簡化 HTML 結構並壓縮原始碼,以便加快渲染和下載速度。然而,與圖片和影片相比,HTML 檔案大小微不足道,而且如今瀏覽器的渲染速度非常快。如果您的 HTML 原始碼如此龐大和複雜,以至於造成渲染和下載效能問題,您可能面臨更大的問題,應該著手簡化並拆分內容。

可替換元素的響應式處理

響應式設計徹底改變了 Web 內容在不同裝置上的佈局方式。它實現的一個關鍵優勢是動態切換針對不同螢幕尺寸最佳化的佈局,例如寬屏佈局與窄屏(移動)佈局。它還可以根據其他裝置屬性(例如解析度或對亮色或暗色主題的偏好)動態切換內容。

所謂的“移動優先”技術可以確保預設佈局適用於小螢幕裝置,因此移動裝置只需下載適合其螢幕的影像,而無需承擔下載更大桌面影像的效能損失。然而,由於這在 CSS 中使用媒體查詢進行控制,因此它只能積極影響 CSS 中載入的影像的效能。

在以下部分中,我們將總結如何實現響應式可替換元素。您可以在 HTML 影片和音訊以及 響應式影像指南中找到有關這些實現的更多詳細資訊。

透過 srcset 提供不同的影像解析度

為了根據裝置的解析度和視口大小提供相同影像的不同解析度版本,您可以使用 srcsetsizes 屬性。

此示例為不同的螢幕寬度提供不同大小的圖片

html
<img
  srcset="480w.jpg 480w, 800w.jpg 800w"
  sizes="(width <= 600px) 480px,
         800px"
  src="800w.jpg"
  alt="Family portrait" />

srcset 提供源影像的固有大小及其檔名,而 sizes 提供媒體查詢以及在每種情況下需要填充的影像槽位寬度。然後,瀏覽器決定為每個槽位載入哪些影像。例如,如果螢幕寬度為 600px 或更小,則 width <= 600px 為真,因此,要填充的槽位被認為是 480px。在這種情況下,瀏覽器很可能會選擇載入 480w.jpg 檔案(480px 寬的影像)。這有助於提高效能,因為瀏覽器不會載入超出所需大小的影像。

此示例為不同的螢幕解析度提供不同的解析度影像

html
<img
  srcset="320w.jpg, 480w.jpg 1.5x, 640w.jpg 2x"
  src="640w.jpg"
  alt="Family portrait" />

1.5x2x 等是相對解析度指示器。如果影像樣式設定為 320px 寬(例如在 CSS 中使用 width: 320px),如果裝置是低解析度(每個 CSS 畫素一個裝置畫素),瀏覽器將載入 320w.jpg;如果裝置是高解析度(每個 CSS 畫素兩個或更多裝置畫素),瀏覽器將載入 640x.jpg

在兩種情況下,如果瀏覽器不支援 src/srcsetsrc 屬性都會提供要載入的預設影像。

為圖片和影片提供不同的來源

<picture> 元素建立在傳統的 <img> 元素之上,允許您為不同情況提供多個不同的源。例如,如果佈局很寬,您可能需要一個寬影像;如果佈局很窄,您可能需要一個在該上下文中仍然有效的更窄影像。

當然,這也可以為移動裝置提供更小的資料下載量,有助於提高效能。

例如:

html
<picture>
  <source media="(width < 800px)" srcset="narrow-banner-480w.jpg" />
  <source media="(width >= 800px)" srcset="wide-banner-800w.jpg" />
  <img src="large-banner-800w.jpg" alt="Dense forest scene" />
</picture>

<source> 元素在 media 屬性中包含媒體查詢。如果媒體查詢返回 true,則載入其 <source> 元素的 srcset 屬性中引用的影像。在上面的示例中,如果視口寬度小於 800px,則載入 narrow-banner-480w.jpg 影像。另請注意 <picture> 元素如何包含一個 <img> 元素,該元素為不支援 <picture> 的瀏覽器提供一個預設載入的影像。

請注意此示例中 srcset 屬性的使用。如上一節所示,您可以為每個影像源提供不同的解析度。

<video> 元素在提供不同源方面以類似方式工作

html
<video controls>
  <source src="video/smaller.mp4" type="video/mp4" />
  <source src="video/smaller.webm" type="video/webm" />
  <source src="video/larger.mp4" type="video/mp4" media="(width >= 800px)" />
  <source src="video/larger.webm" type="video/webm" media="(width >= 800px)" />

  <!-- fallback for browsers that don't support video element -->
  <a href="video/larger.mp4">download video</a>
</video>

然而,為影像和影片提供來源之間存在一些關鍵差異

  • 在上面的示例中,我們使用的是 src 而不是 srcset;您無法透過 srcset 為影片指定不同的解析度。
  • 相反,您在不同的 <source> 元素中指定不同的解析度。
  • 請注意,我們還在不同的 <source> 元素中指定了不同的影片格式,每種格式都透過其 MIME 型別在 type 屬性中標識。瀏覽器將載入它們遇到的第一個支援的影片,其中媒體查詢測試返回 true。

圖片懶載入

一個非常有用的效能最佳化技術是懶載入。這指的是在 HTML 渲染時不是立即載入所有影像,而是僅在它們實際在視口中對使用者可見(或即將可見)時才載入它們。這意味著立即可見/可用的內容可以更快地投入使用,而後續內容只有在滾動到時才渲染其影像,並且瀏覽器不會浪費頻寬載入使用者永遠不會看到的影像。

過去,懶載入通常使用 JavaScript 處理,但現在瀏覽器提供了 loading 屬性,可以指示瀏覽器自動懶載入影像

html
<img src="800w.jpg" alt="Family portrait" loading="lazy" />

有關詳細資訊,請參閱 web.dev 上的Web 瀏覽器級別的影像懶載入

您還可以使用 preload 屬性懶載入影片內容。例如

html
<video controls preload="none" poster="poster.jpg">
  <source src="video.webm" type="video/webm" />
  <source src="video.mp4" type="video/mp4" />
</video>

preload 的值設定為 none 告訴瀏覽器在使用者決定播放影片之前不要預載入任何影片資料,這顯然有利於效能。相反,它只會顯示由 poster 屬性指示的影像。不同的瀏覽器有不同的預設影片載入行為,因此明確指定是個好習慣。

有關詳細資訊,請參閱 web.dev 上的透過音訊和影片預載入實現快速播放

處理嵌入內容

網頁中嵌入來自其他來源的內容非常常見。這最常用於在網站上顯示廣告以創收——廣告通常由某種第三方公司生成並嵌入到您的頁面中。其他用途可能包括:

  • 顯示使用者在多個頁面上可能需要的共享內容,例如購物車或個人資料資訊。
  • 顯示與組織主站點相關的第三方內容,例如社交媒體帖子提要。

嵌入內容最常使用 <iframe> 元素,儘管也存在其他不常用的嵌入元素,例如 <object><embed>。在本節中,我們將重點討論 <iframe>

關於使用 <iframe> 最重要和最關鍵的建議是:“除非絕對必要,否則不要使用嵌入式 <iframe>。”如果您正在建立一個包含多個不同資訊窗格的頁面,將它們分解成單獨的頁面並載入到不同的 <iframe> 中可能聽起來在組織上是合理的。然而,這在效能和其他方面都存在許多問題:

  • 將內容載入到 <iframe> 中的成本遠高於將內容作為同一直接頁面的一部分載入——它不僅需要額外的 HTTP 請求來載入內容,而且瀏覽器還需要為每個 <iframe> 建立一個單獨的頁面例項。每個 <iframe> 實際上都是一個嵌入在頂級網頁中的獨立網頁例項。
  • 承接上一點,您還需要為每個不同的 <iframe> 分別處理任何 CSS 樣式或 JavaScript 操作(除非嵌入頁面來自同一源),這變得複雜得多。您無法使用應用於頂級頁面的 CSS 和 JavaScript 來定位嵌入內容,反之亦然。這是一項明智的安全措施,也是 Web 的基本原則。想象一下,如果第三方嵌入內容可以隨意運行針對其嵌入的任何頁面的指令碼,您可能會遇到所有問題!
  • 每個 <iframe> 還需要單獨載入任何共享資料和媒體檔案——您不能在不同的頁面嵌入之間共享快取資源(同樣,除非嵌入頁面來自同一源)。這可能導致頁面使用的頻寬遠超您的預期。

建議將內容放入單個頁面。如果您想在頁面更改時動態拉取新內容,將其載入到同一頁面而不是放入 <iframe> 仍然對效能更好。例如,您可以使用 fetch() 方法獲取新資料,然後使用一些 DOM 指令碼將其注入頁面。有關更多資訊,請參閱使用 JavaScript 發出網路請求DOM 指令碼簡介

注意: 如果您控制內容並且內容相對簡單,您可以考慮在 src 屬性中使用 base-64 編碼的內容來填充 <iframe>,甚至將原始 HTML 插入 srcdoc 屬性(有關更多資訊,請參閱Iframe 效能第二部分:好訊息)。

如果必須使用 <iframe>,請節制使用。

Iframes 懶載入

<img> 元素一樣,您也可以使用 loading 屬性指示瀏覽器懶載入最初在螢幕外的 <iframe> 內容,從而提高效能

html
<iframe src="https://example.com" loading="lazy" width="600" height="400">
</iframe>

有關更多資訊,請參閱是時候懶載入螢幕外 iframes 了!

處理資源載入順序

資源載入順序對於最大化感知和實際效能至關重要。當網頁載入時

  1. HTML 通常首先被解析,按照其在頁面中出現的順序。
  2. 任何發現的 CSS 都被解析以理解需要應用於頁面的樣式。在此期間,連結的資源(如影像和 Web 字型)開始被獲取。
  3. 任何發現的 JavaScript 都會被解析、評估並在頁面上執行。預設情況下,這會阻止解析出現在 JavaScript 所在 <script> 元素之後的 HTML。
  4. 稍後,瀏覽器根據應用於每個 HTML 元素的 CSS 確定其樣式。
  5. 然後將樣式化的結果繪製到螢幕上。

注意: 這只是對所發生事情的一個非常簡化的描述,但它確實能讓您有所瞭解。

各種 HTML 功能允許您修改資源載入方式以提高效能。我們現在將探討其中一些功能。

處理 JavaScript 載入

解析和執行 JavaScript 會阻塞後續 DOM 內容的解析。這增加了內容渲染並供頁面使用者使用的時間。一個小的指令碼不會有太大影響,但請考慮現代 Web 應用程式往往 JavaScript 程式碼量很大。

預設 JavaScript 解析行為的另一個副作用是,如果正在渲染的指令碼依賴於頁面中稍後出現的 DOM 內容,您將收到錯誤。

例如,想象頁面上的一個簡單段落

html
<p>My paragraph</p>

現在想象一個包含以下程式碼的 JavaScript 檔案

js
const pElem = document.querySelector("p");

pElem.addEventListener("click", () => {
  alert("You clicked the paragraph");
});

我們可以透過在 <script> 元素中引用它來將此指令碼應用於頁面,如下所示

html
<script src="index.js"></script>

如果我們按照原始碼順序將此 <script> 元素放在 <p> 元素之前(例如,在 <head> 元素中),頁面將丟擲錯誤(例如,Chrome 會在控制檯中報告“Uncaught TypeError: Cannot read properties of null (reading 'addEventListener')”)。發生這種情況是因為指令碼依賴於 <p> 元素才能工作,但在指令碼解析時,<p> 元素在頁面上尚不存在。它尚未被渲染。

您可以透過將 <script> 元素放在 <p> 元素之後(例如,在文件主體的末尾),或者透過在合適的事件處理程式中執行程式碼(例如,在 DOM 完全解析後觸發的 DOMContentLoaded 事件上執行)來解決上述問題。

然而,這並不能解決等待指令碼載入的問題。透過向 <script> 元素新增 async 屬性可以實現更好的效能

html
<script async src="index.js"></script>

這使得指令碼與 DOM 解析並行獲取,因此它同時準備就緒,並且不會阻塞渲染,從而提高效能。

注意: 還有另一個屬性 defer,它使指令碼在文件解析後但在觸發 DOMContentLoaded 之前執行。這與 async 具有類似的效果。

另一個 JavaScript 載入處理技巧是將您的指令碼拆分成程式碼模組,並在需要時載入每個部分,而不是將所有程式碼放入一個巨大的指令碼中並在開始時全部載入。這是使用JavaScript 模組完成的。閱讀連結文章以獲取詳細指南。

使用 rel="preload" 預載入內容

從您的 HTML、CSS 和 JavaScript 連結的其他資源(如圖片、影片或字型檔案)的獲取也可能導致效能問題,阻礙您的程式碼執行並減慢體驗。緩解此類問題的一種方法是使用 rel="preload"<link> 元素轉換為預載入器。例如

html
<link rel="preload" href="sintel-short.mp4" as="video" type="video/mp4" />

瀏覽器遇到 rel="preload" 連結時,會盡快獲取引用的資源並將其儲存在瀏覽器快取中,以便在後續程式碼中引用時能更快地使用。預載入使用者在頁面早期會遇到的高優先順序資源非常有用,這樣可以使體驗儘可能流暢。

有關使用 rel="preload" 的詳細資訊,請參閱以下文章

注意: 您也可以使用 rel="preload" 預載入 CSS 和 JavaScript 檔案。

注意: 還有其他一些 rel 值也旨在加速頁面載入的各個方面:dns-prefetchpreconnectmodulepreloadprefetch。訪問連結頁面並瞭解它們的功能。

另見