HTML 效能最佳化
HTML 預設情況下是快速且可訪問的。作為開發者,我們的工作是在建立或編輯 HTML 程式碼時確保我們保留這兩個特性。例如,當 <video> 嵌入的檔案大小過大,或者 JavaScript 解析阻塞了關鍵頁面元素的渲染時,就會出現問題。本文將帶您瞭解可以大幅提升網頁質量的關鍵 HTML 效能特性。
| 先決條件 | 已安裝基本軟體,並具備對 客戶端 Web 技術 的基本瞭解。 |
|---|---|
| 目標 | 瞭解 HTML 對網站效能的影響,以及如何最佳化您的 HTML 以提高效能。 |
是否最佳化
HTML 效能的關鍵問題
從效能角度來看,HTML 很簡單——它主要是文字,尺寸很小,因此通常下載和渲染速度很快。可能影響網頁效能的關鍵問題包括
- 影像和影片檔案的大小:重要的是要考慮如何處理替換元素(例如
<img>和<video>)的內容。影像和影片檔案很大,會顯著增加頁面的重量。因此,務必最大限度地減少下載到使用者裝置上的位元組數(例如,為移動裝置提供更小的影像)。您還需要考慮透過僅在需要時載入頁面上的影像和影片來提高感知效能。 - 嵌入內容的交付:這通常是嵌入在
<iframe>元素中的內容。將內容載入到<iframe>中會嚴重影響效能,因此應謹慎考慮。 - 資源載入順序:為了最大限度地提高感知效能和實際效能,應首先按其在頁面上的顯示順序載入 HTML。然後,您可以使用各種功能來影響資源載入順序,以獲得更好的效能。例如,您可以預載入關鍵 CSS 和字型,但將非關鍵 JavaScript 推遲到稍後。
注意:有人認為簡化 HTML 結構和 壓縮 原始碼可以加快渲染和下載速度。但是,與影像和影片相比,HTML 檔案大小可以忽略不計,而且如今瀏覽器渲染速度非常快。如果您的 HTML 原始碼非常龐大而複雜,導致渲染和下載效能下降,那麼您可能遇到了更大的問題,應該嘗試簡化它並拆分內容。
對替換元素的響應式處理
響應式設計 徹底改變了跨不同裝置處理 Web 內容佈局的方式。它實現的一項關鍵優勢是可以動態切換針對不同螢幕尺寸最佳化的佈局,例如寬屏佈局與窄屏(移動)佈局。它還可以根據其他裝置屬性(例如解析度或對淺色或深色配色方案的偏好)處理內容的動態切換。
所謂的“移動優先”技術可以確保預設佈局適合小螢幕裝置,因此移動裝置只需下載適合其螢幕的影像,而無需承擔下載更大桌面影像的效能損失。但是,由於這是使用 媒體查詢 在您的 CSS 中控制的,因此它只能積極影響 CSS 中載入的影像的效能。
在以下部分,我們將總結如何在響應式替換元素中實現響應式設計。您可以在 影片和音訊內容 和 響應式影像 指南中找到有關這些實現的更多詳細資訊。
透過 srcset 提供不同的影像解析度
要根據裝置的解析度和視口大小提供相同影像的不同解析度版本,可以使用 srcset 和 sizes 屬性。
此示例為不同螢幕寬度提供了不同尺寸的影像
<img
srcset="480w.jpg 480w, 800w.jpg 800w"
sizes="(max-width: 600px) 480px,
800px"
src="800w.jpg"
alt="Family portrait" />
srcset 提供了源影像的內在尺寸及其檔名,sizes 提供了媒體查詢以及在每種情況下需要填充的影像插槽寬度。然後瀏覽器會決定為每個插槽載入哪些影像。例如,如果螢幕寬度為 600px 或更小,則 max-width: 600px 為真,因此要填充的插槽為 480px。在這種情況下,瀏覽器可能會選擇載入 480w.jpg 檔案(480 畫素寬的影像)。這有助於提高效能,因為瀏覽器不會載入超過其所需的影像。
此示例為不同螢幕解析度提供了不同解析度的影像
<img
srcset="320w.jpg, 480w.jpg 1.5x, 640w.jpg 2x"
src="640w.jpg"
alt="Family portrait" />
1.5x、2x 等是相對解析度指示符。如果影像的樣式設定為 320 畫素寬(例如,在 CSS 中使用 width: 320px),則如果裝置為低解析度(每個 CSS 畫素一個裝置畫素),瀏覽器將載入 320w.jpg,或者如果裝置為高解析度(每個 CSS 畫素兩個或更多裝置畫素),則載入 640x.jpg。
在這兩種情況下,src 屬性都提供了一個預設影像,如果瀏覽器不支援 src/srcset,則將載入該影像。
為影像和影片提供不同的來源
<picture> 元素建立在傳統的 <img> 元素之上,允許您為不同的情況提供多個不同的來源。例如,如果佈局很寬,您可能想要一個寬影像,如果佈局很窄,您可能想要一個仍然適合該上下文更窄的影像。
當然,這也可以為移動裝置提供更小的下載資訊量,從而有助於提高效能。
一個例子如下
<picture>
<source media="(max-width: 799px)" srcset="narrow-banner-480w.jpg" />
<source media="(min-width: 800px)" srcset="wide-banner-800w.jpg" />
<img src="large-banner-800w.jpg" alt="Dense forest scene" />
</picture>
<source> 元素在 media 屬性內包含媒體查詢。如果媒體查詢返回真,則載入其 <source> 元素的 srcset 屬性中引用的影像。在上面的示例中,如果視口寬度為 799px 或更小,則載入 narrow-banner-480w.jpg 影像。還要注意 <picture> 元素如何包含一個 <img> 元素,它提供了一個預設影像,以供不支援 <picture> 的瀏覽器載入。
注意本例中使用 srcset 屬性。如前一部分所示,您可以為每個影像源提供不同的解析度。
<video> 元素在提供不同的來源方面的工作方式類似
<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="(min-width: 800px)" />
<source
src="video/larger.webm"
type="video/webm"
media="(min-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>元素內指定不同的影片格式,每個格式都透過其type屬性內的 MIME 型別來標識。瀏覽器將載入他們遇到的第一個支援的格式,其中媒體查詢測試返回真。
延遲載入影像
提高效能的一個非常有用的技術是延遲載入。它指的是不立即在 HTML 渲染時載入所有影像,而是在影像實際出現在使用者視口中(或即將出現在視口中)時才載入它們。這意味著立即可見/可用的內容可以更快地使用,而後續內容的影像僅在滾動到時才渲染,瀏覽器不會浪費頻寬載入使用者永遠不會看到的影像。
延遲載入過去通常使用 JavaScript 處理,但瀏覽器現在可以使用 loading 屬性來指示瀏覽器自動延遲載入影像
<img src="800w.jpg" alt="Family portrait" loading="lazy" />
請參閱 web.dev 上的 瀏覽器級影像延遲載入,以獲取詳細的資訊。
您還可以透過使用 preload 屬性來延遲載入影片內容。例如
<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>的任何 CSS 樣式或 JavaScript 操作(除非嵌入頁面來自同一個來源),這會變得更加複雜。你無法使用應用於頂級頁面的 CSS 和 JavaScript 來定位嵌入內容,反之亦然。這是一種合理的安全措施,是網路的基礎。想象一下,如果第三方嵌入內容可以任意地對它嵌入的任何頁面執行指令碼,你可能會遇到多少問題! - 每個
<iframe>還需要分別載入任何共享資料和媒體檔案——你無法跨不同的頁面嵌入共享快取的資產(同樣,除非嵌入頁面來自同一個來源)。這會導致頁面使用比你預期的更多的頻寬。
建議將內容放在一個頁面中。如果你想在頁面更改時動態地拉入新內容,為了效能,最好將其載入到同一頁面,而不是將其放入<iframe>中。例如,你可以使用fetch()方法獲取新資料,然後使用一些 DOM 指令碼將其注入頁面。有關更多資訊,請參閱從伺服器獲取資料和操作文件。
注意:如果你控制內容並且內容相對簡單,你可以考慮在src屬性中使用 base-64 編碼的內容來填充<iframe>,甚至可以將原始 HTML 插入到srcdoc屬性中(有關更多資訊,請參閱Iframe 效能第 2 部分:好訊息)。
如果你必須使用<iframe>,那麼請謹慎使用。
延遲載入 iframe
與<img>元素相同,你也可以使用loading屬性來指示瀏覽器延遲載入最初處於螢幕外的<iframe>內容,從而提高效能。
<iframe src="https://example.com" loading="lazy" width="600" height="400">
</iframe>
有關更多資訊,請參閱是時候延遲載入螢幕外的 iframe 了!。
處理資源載入順序
資源載入的順序對於最大化感知效能和實際效能很重要。當載入網頁時
- HTML 通常首先被解析,解析順序與它在頁面上的顯示順序相同。
- 解析任何找到的 CSS 以瞭解需要應用於頁面的樣式。在此期間,連結的資產(如影像和網路字型)開始被獲取。
- 解析任何找到的 JavaScript,並針對頁面進行評估和執行。預設情況下,這會阻止解析出現在
<script>元素之後的 HTML,而這些元素是遇到 JavaScript 的地方。 - 稍後,瀏覽器將根據應用於它的 CSS 計算每個 HTML 元素的樣式。
- 然後將樣式化的結果繪製到螢幕上。
注意:這只是對發生情況的簡化說明,但它確實讓你瞭解了一些情況。
各種 HTML 功能允許你修改資源載入的方式,以提高效能。我們現在將探索其中的一些功能。
處理 JavaScript 載入
解析和執行 JavaScript 會阻止後續 DOM 內容的解析。這會增加渲染該內容以及使用者可以使用該內容的時間。一個小指令碼不會有太大區別,但要考慮到現代 Web 應用程式往往非常依賴 JavaScript。
預設 JavaScript 解析行為的另一個副作用是,如果正在渲染的指令碼依賴於頁面後面出現的 DOM 內容,你將收到錯誤。
例如,想象一下頁面上的一個簡單段落
<p>My paragraph</p>
現在想象一個包含以下程式碼的 JavaScript 檔案
const pElem = document.querySelector("p");
pElem.addEventListener("click", () => {
alert("You clicked the paragraph");
});
我們可以透過在<script>元素中引用它來將此指令碼應用於頁面
<script src="index.js"></script>
如果我們將此<script>元素放在原始碼順序中<p>元素之前(例如,在<head>元素中),頁面將丟擲錯誤(例如,Chrome 在控制檯中報告“Uncaught TypeError: Cannot read properties of null (reading 'addEventListener')”。這是因為指令碼依賴於<p>元素才能工作,但在解析指令碼時,頁面上不存在<p>元素。它還沒有被渲染。
你可以透過將<script>元素放在<p>元素之後(例如在文件主體末尾)來解決上述問題,或者透過在合適的事件處理程式中執行程式碼來解決(例如,在DOMContentLoaded上執行它,該事件在 DOM 完全解析後觸發)。
但是,這並不能解決等待指令碼載入的問題。透過向<script>元素新增async屬性可以實現更好的效能
<script async src="index.js"></script>
這會導致指令碼與 DOM 解析並行獲取,因此指令碼在同一時間準備就緒,並且不會阻止渲染,從而提高效能。
注意:還有另一個屬性defer,它會導致指令碼在文件解析完成後但DOMContentLoaded觸發之前執行。這與async具有類似的效果。
另一個 JavaScript 載入處理技巧是將你的指令碼拆分為程式碼模組,並在需要時載入每個部分,而不是將所有程式碼都放在一個巨大的指令碼中,並在開頭載入所有程式碼。這是使用JavaScript 模組完成的。閱讀連結的文章以獲取詳細指南。
使用 rel="preload" 預載入內容
從你的 HTML、CSS 和 JavaScript 中連結的其他資源(如影像、影片或字型檔案)的獲取也會導致效能問題,阻止你的程式碼執行並減慢體驗速度。緩解此類問題的一種方法是使用rel="preload"將<link>元素變成預載入器。例如
<link rel="preload" href="sintel-short.mp4" as="video" type="video/mp4" />
遇到rel="preload"連結時,瀏覽器會盡快獲取引用的資源,並將其放入瀏覽器快取中,以便在後續程式碼中引用時可以更快地使用。預載入使用者在頁面早期遇到的高優先順序資源很有用,這樣可以使體驗儘可能平滑。
有關使用rel="preload"的詳細資訊,請參閱以下文章
rel="preload"- 預載入關鍵資產以提高載入速度 在 web.dev 上(2020 年)
注意:你也可以使用rel="preload"預載入 CSS 和 JavaScript 檔案。
注意:還有其他rel值也旨在加速頁面的載入速度:dns-prefetch、preconnect、modulepreload、prefetch和prerender。轉到連結的頁面並瞭解它們的功能。