2021 年前端性能:資產優化
已發表: 2022-03-10本指南得到了我們在 LogRocket 的朋友的大力支持,LogRocket 是一項結合前端性能監控、會話重放和產品分析的服務,可幫助您建立更好的客戶體驗。 LogRocket跟踪關鍵指標,包括。 DOM 完成、第一個字節的時間、第一個輸入延遲、客戶端 CPU 和內存使用情況。 立即免費試用 LogRocket。
目錄
- 準備:計劃和指標
- 設定切合實際的目標
- 定義環境
- 資產優化
- 構建優化
- 交付優化
- 網絡、HTTP/2、HTTP/3
- 測試和監控
- 快速獲勝
- 一切都在一頁上
- 下載清單(PDF、Apple Pages、MS Word)
- 訂閱我們的電子郵件通訊不要錯過下一個指南。
資產優化
- 使用 Brotli 進行純文本壓縮。
2015 年,谷歌推出了 Brotli,這是一種新的開源無損數據格式,現在所有現代瀏覽器都支持這種格式。 為 Brotli 實現編碼器和解碼器的開源 Brotli 庫具有 11 個預定義的編碼器質量級別,更高的質量級別需要更多的 CPU 以換取更好的壓縮比。 較慢的壓縮最終會導致更高的壓縮率,但 Brotli 仍然可以快速解壓縮。 值得注意的是,壓縮級別為 4 的 Brotli 比 Gzip 更小且壓縮速度更快。在實踐中,Brotli 似乎比 Gzip 更有效。 意見和經驗各不相同,但如果您的網站已經使用 Gzip 進行了優化,您可能會期望在大小縮減和 FCP 時間方面至少有一位數的改進,最多也有兩位數的改進。 您還可以估計您的站點的 Brotli 壓縮節省。
只有當用戶通過 HTTPS 訪問網站時,瀏覽器才會接受 Brotli。 Brotli 受到廣泛支持,許多 CDN 都支持它(Akamai、Netlify Edge、AWS、KeyCDN、Fastly(目前僅作為直通)、Cloudflare、CDN77),即使在尚不支持的 CDN 上也可以啟用 Brotli (與服務人員)。
問題是,由於使用 Brotli 以高壓縮級別壓縮所有資產的成本很高,因此許多託管服務提供商不能完全使用它,因為它產生的巨大成本開銷。 事實上,在最高級別的壓縮下,Brotli非常慢,以至於服務器在等待動態壓縮資產時開始發送響應所需的時間可能會抵消文件大小的任何潛在收益。 (但如果您在構建期間有時間使用靜態壓縮,當然,更高的壓縮設置是首選。)
各種壓縮方法的後端時間比較。 不出所料,Brotli 比 gzip 慢(目前)。 (大預覽) 不過,這可能正在改變。 Brotli 文件格式包括一個內置的靜態字典,除了包含多種語言的各種字符串外,它還支持對這些單詞應用多種轉換的選項,增加了它的多功能性。 在他的研究中,Felix Hanau 發現了一種改進 5 到 9 級壓縮的方法,方法是使用“比默認字典更專業的子集”並依靠
Content-Type
標頭告訴壓縮器是否應該使用HTML、JavaScript 或 CSS 的字典。 結果是“在使用有限的字典使用方法以高壓縮級別壓縮 Web 內容時,性能影響可以忽略不計(CPU 比正常情況下的 12% 多 1% 到 3%)。”使用改進的字典方法,我們可以在更高的壓縮級別上更快地壓縮資產,同時只使用 1% 到 3% 的 CPU。 通常,壓縮級別 6 比 5 最多會使 CPU 使用率增加 12%。 (大預覽) 最重要的是,通過 Elena Kirilenko 的研究,我們可以使用以前的壓縮工件實現快速高效的 Brotli 再壓縮。 根據 Elena 的說法,“一旦我們通過 Brotli 壓縮了資產,並且我們嘗試動態壓縮動態內容,其中的內容類似於我們提前可用的內容,我們就可以顯著縮短壓縮時間。 "
這種情況多久發生一次? 例如,交付JavaScript 捆綁包子集(例如,當部分代碼已經緩存在客戶端或使用 WebBundles 提供動態捆綁包時)。 或者使用基於預先知道的模板的動態 HTML,或者動態子集的 WOFF2 字體。 根據 Elena 的說法,當刪除 10% 的內容時,我們可以獲得 5.3% 的壓縮改進和 39% 的壓縮速度改進,當刪除 50% 的內容時,壓縮率提高了 3.2%,壓縮速度提高了 26%。
Brotli 壓縮越來越好,所以如果你能繞過動態壓縮靜態資產的成本,那絕對是值得的。 不用說,Brotli 可以用於任何純文本負載——HTML、CSS、SVG、JavaScript、JSON 等等。
注意:截至 2021 年初,大約 60% 的 HTTP 響應是在沒有基於文本的壓縮的情況下交付的,其中 30.82% 使用 Gzip 壓縮,9.1% 使用 Brotli 壓縮(無論是在移動設備上還是在桌面上)。 例如,23.4% 的 Angular 頁面沒有被壓縮(通過 gzip 或 Brotli)。 然而,通常打開壓縮是通過簡單的開關翻轉來提高性能的最簡單的方法之一。
策略? 在最高級別使用 Brotli+Gzip 預壓縮靜態資產,並在級別 4-6 使用 Brotli 動態壓縮(動態)HTML。 確保服務器正確處理 Brotli 或 Gzip 的內容協商。

- 我們是否使用自適應媒體加載和客戶端提示?
它來自舊新聞的土地,但它始終是一個很好的提醒,使用帶有srcset
、sizes
和<picture>
元素的響應式圖像。 特別是對於媒體佔用量大的網站,我們可以通過自適應媒體加載(在本例中為 React + Next.js)更進一步,為慢速網絡和低內存設備提供輕量體驗,為快速網絡和高內存設備提供完整體驗-內存設備。 在 React 的上下文中,我們可以通過服務器上的客戶端提示和客戶端上的 react-adaptive-hooks 來實現它。隨著客戶提示的廣泛採用,響應式圖像的未來可能會發生巨大變化。 客戶端提示是 HTTP 請求標頭字段,例如
DPR
、Viewport-Width
、Width
、Save-Data
、Accept
(指定圖像格式首選項)等。 他們應該通知服務器有關用戶瀏覽器、屏幕、連接等的細節。因此,服務器可以決定如何用適當大小的圖像填充佈局,並僅以所需格式提供這些圖像。 通過客戶端提示,我們將資源選擇從 HTML 標記移到客戶端和服務器之間的請求-響應協商中。
正在使用的自適應媒體服務。 我們向離線用戶發送帶文本的佔位符,向 2G 用戶發送低分辨率圖像,向 3G 用戶發送高分辨率圖像,向 4G 用戶發送高清視頻。 通過在 20 美元的功能手機上快速加載網頁。 (大預覽) 正如 Ilya Grigorik 不久前指出的那樣,客戶提示完成了圖片 - 它們不是響應式圖像的替代品。 “
<picture>
元素在 HTML 標記中提供了必要的藝術方向控制。客戶端提示為生成的圖像請求提供註釋,從而實現資源選擇自動化。Service Worker 在客戶端提供完整的請求和響應管理功能。”例如,服務工作者可以將新的客戶端提示標頭值附加到請求中,重寫 URL 並將圖像請求指向 CDN,根據連接性和用戶偏好調整響應等。它不僅適用於圖像資產,而且適用於幾乎所有其他請求也是如此。
對於支持客戶端提示的客戶端,可以測量到圖像節省 42% 的字節和 70th+ 百分位數的 1MB+ 更少的字節。 在 Smashing Magazine 上,我們也可以測量到 19-32% 的改進。 基於 Chromium 的瀏覽器支持客戶端提示,但在 Firefox 中仍在考慮中。
但是,如果您同時為客戶端提示提供正常的響應式圖像標記和
<meta>
標記,則支持的瀏覽器將評估響應式圖像標記並使用客戶端提示 HTTP 標頭請求適當的圖像源。 - 我們是否使用響應式圖像作為背景圖像?
我們當然應該! 使用image-set
,現在 Safari 14 和除 Firefox 之外的大多數現代瀏覽器都支持,我們也可以提供響應式背景圖像:background-image: url("fallback.jpg"); background-image: image-set( "photo-small.jpg" 1x, "photo-large.jpg" 2x, "photo-print.jpg" 600dpi);
基本上,我們可以有條件地提供具有
1x
描述符的低分辨率背景圖像,以及具有2x
描述符的高分辨率圖像,甚至是具有600dpi
描述符的打印質量圖像。 但請注意:瀏覽器不會為輔助技術提供有關背景圖像的任何特殊信息,因此理想情況下,這些照片只是裝飾。 - 我們使用 WebP 嗎?
圖像壓縮通常被認為是一種快速的勝利,但在實踐中它仍然沒有得到充分利用。 當然,圖像不會阻塞渲染,但它們會嚴重影響 LCP 分數,而且通常它們對於正在使用它們的設備來說太重太大了。所以至少,我們可以探索為我們的圖像使用 WebP 格式。 事實上,隨著 Apple 在 Safari 14 中增加對 WebP 的支持,WebP 傳奇已接近尾聲。因此,經過多年的討論和辯論,截至今天,所有現代瀏覽器都支持 WebP。 因此,如果需要(請參閱 Andreas Bovens 的代碼片段)或使用內容協商(使用
Accept
標頭),我們可以使用<picture>
元素和 JPEG 後備來提供 WebP 圖像。不過,WebP 並非沒有缺點。 雖然 WebP 圖像文件的大小與等效的 Guetzli 和 Zopfli 相比,但該格式不支持像 JPEG 這樣的漸進式渲染,這就是為什麼用戶可以使用良好的 JPEG 更快地看到完成的圖像,儘管 WebP 圖像可能會通過網絡變得更快。 使用 JPEG,我們可以用一半甚至四分之一的數據提供“體面”的用戶體驗,然後再加載其餘的數據,而不是像 WebP 那樣使用半空圖像。
您的決定將取決於您追求的目標:使用 WebP,您將減少有效負載,使用 JPEG,您將提高感知性能。 您可以在 Google 的 Pascal Massimino 的 WebP Rewind 演講中了解有關 WebP 的更多信息。
要轉換為 WebP,您可以使用 WebP Converter、cwebp 或 libwebp。 Ire Aderinokun 也有一個非常詳細的關於將圖像轉換為 WebP 的教程——Josh Comeau 在他關於擁抱現代圖像格式的文章中也是如此。
關於 WebP 的徹底討論:Pascal Massimino 的 WebP Rewind。 (大預覽) Sketch 原生支持 WebP,並且可以使用 Photoshop 的 WebP 插件從 Photoshop 中導出 WebP 圖像。 但也有其他選擇。
如果您使用的是 WordPress 或 Joomla,有一些擴展可以幫助您輕鬆實現對 WebP 的支持,例如 Optimus 和 WordPress 的 Cache Enabler 以及 Joomla 自己支持的擴展(通過 Cody Arsenault)。 您還可以使用 React、樣式化組件或 gatsby-image 抽像出
<picture>
元素。啊——不要臉的插頭! — Jeremy Wagner 甚至出版了一本關於 WebP 的 Smashing 書,您可能想看看您是否對 WebP 周圍的一切感興趣。
- 我們使用 AVIF 嗎?
您可能已經聽說了一個重大消息:AVIF 已經登陸。 它是一種源自 AV1 視頻關鍵幀的新圖像格式。 它是一種開放、免版稅的格式,支持有損和無損壓縮、動畫、有損 Alpha 通道,並且可以處理銳利的線條和純色(這是 JPEG 的一個問題),同時提供更好的結果。事實上,與 WebP 和 JPEG 相比, AVIF 的性能要好得多,在相同的 DSSIM 下(使用近似人類視覺的算法,兩個或多個圖像之間的(不)相似性),文件大小中值節省高達 50%。 事實上,在他關於優化圖像加載的詳盡文章中,Malte Ubl 指出,AVIF“在一個非常重要的方面始終優於 JPEG。這與 WebP 不同,WebP 並不總是產生比 JPEG 更小的圖像,實際上可能是一個網絡 -由於缺乏對漸進式加載的支持而導致的損失。”
我們可以將 AVIF 用作漸進增強,將 WebP 或 JPEG 或 PNG 傳遞給舊版瀏覽器。 (大預覽)。 請參閱下面的純文本視圖。 具有諷刺意味的是,AVIF 的性能甚至比大型 SVG 還要好,儘管它當然不應被視為 SVG 的替代品。 它也是最早支持 HDR 顏色支持的圖像格式之一; 提供更高的亮度、色位深度和色域。 唯一的缺點是目前 AVIF 不支持漸進式圖像解碼(還沒有?),與 Brotli 類似,高壓縮率編碼目前相當慢,儘管解碼速度很快。
AVIF 目前在 Chrome、Firefox 和 Opera 中得到支持,而對 Safari 的支持預計很快就會到來(因為 Apple 是創建 AV1 的小組的成員)。
那麼,如今提供圖像的最佳方式是什麼? 對於插圖和矢量圖,(壓縮的)SVG 無疑是最佳選擇。 對於照片,我們使用帶有
picture
元素的內容協商方法。 如果支持 AVIF,我們發送 AVIF 圖像; 如果不是這樣,我們首先回退到 WebP,如果 WebP 也不支持,我們切換到 JPEG 或 PNG 作為後備(如果需要,應用@media
條件):<picture> <source type="image/avif"> <source type="image/webp"> <img src="image.jpg" alt="Photo" width="450" height="350"> </picture>
坦率地說,我們更有可能在
picture
元素中使用一些條件:<picture> <source type="image/avif" /> <source type="image/webp" /> <source type="image/jpeg" /> <img src="fallback-image.jpg" alt="Photo" width="450" height="350"> </picture>
<picture> <source type="image/avif" /> <source type="image/webp" /> <source type="image/jpeg" /> <img src="fallback-image.jpg" alt="Photo" width="450" height="350"> </picture>
對於選擇使用
prefers-reduced-motion
運動的客戶,您可以通過將動畫圖像與靜態圖像交換更進一步:<picture> <source media="(prefers-reduced-motion: reduce)" type="image/avif"></source> <source media="(prefers-reduced-motion: reduce)" type="image/jpeg"></source> <source type="image/avif"></source> <img src="motion.jpg" alt="Animated AVIF"> </picture>
<picture> <source media="(prefers-reduced-motion: reduce)" type="image/avif"></source> <source media="(prefers-reduced-motion: reduce)" type="image/jpeg"></source> <source type="image/avif"></source> <img src="motion.jpg" alt="Animated AVIF"> </picture>
在過去的幾個月裡,AVIF 獲得了相當大的關注:
- 我們可以在 DevTools 的 Rendering 面板中測試 WebP/AVIF 回退。
- 我們可以使用 Squoosh、AVIF.io 和 libavif 對 AVIF 文件進行編碼、解碼、壓縮和轉換。
- 我們可以使用 Jake Archibald 的 AVIF Preact 組件,它在 worker 中解碼 AVIF 文件並將結果顯示在畫布上,
- 為了只向支持的瀏覽器提供 AVIF,我們可以使用 PostCSS 插件和 315B 腳本在您的 CSS 聲明中使用 AVIF。
- 我們可以使用 CSS 和 Cloudlare Workers 逐步交付新的圖像格式,以動態更改返回的 HTML 文檔,從
accept
頭推斷信息,然後根據需要添加webp/avif
等類。 - AVIF 已經在 Cloudinary 中可用(有使用限制),Cloudflare 在 Image Resizing 中支持 AVIF,您可以在 Netlify 中啟用帶有自定義 AVIF 標頭的 AVIF。
- 在動畫方面,AVIF 的表現與 Safari 的
<img src=mp4>
一樣好,總體上優於 GIF 和 WebP,但 MP4 的表現仍然更好。 - 一般來說,對於動畫,AVC1 (h264) > HVC1 > WebP > AVIF > GIF,假設基於 Chromium 的瀏覽器將永遠支持
<img src=mp4>
。 - 您可以在 Netflix 的 Aditya Mavlankar 的 AVIF for Next Generation Image Coding 演講和 Cloudflare 的 Kornel Lesinski 的 AVIF 圖像格式演講中找到有關 AVIF 的更多詳細信息。
- AVIF 的所有內容的絕佳參考:Jake Archibald 關於 AVIF 的綜合帖子已登陸。
那麼未來的AVIF呢? Jon Sneyers 不同意:AVIF 的性能比 JPEG XL 差 60%,JPEG XL 是另一種由 Google 和 Cloudinary 開發的免費開放格式。 事實上,JPEG XL 似乎全面表現得更好。 但是,JPEG XL 仍處於標準化的最後階段,還不能在任何瀏覽器中工作。 (不要與來自優秀的 Internet Explorer 9 次的 Microsoft JPEG-XR 混淆)。

- JPEG/PNG/SVG 是否正確優化?
當您在著陸頁上工作時,英雄圖像的加載速度至關重要編碼器專注於感知性能,並利用 Zopfli 和 WebP 的學習成果。 唯一的缺點:處理時間慢(每百萬像素 CPU 需要一分鐘)。對於 PNG,我們可以使用 Pingo,對於 SVG,我們可以使用 SVGO 或 SVGOMG。 如果您需要從網站快速預覽、複製或下載所有 SVG 資源,svg-grabber 也可以為您完成。
每一篇圖像優化文章都會說明這一點,但保持矢量資源的干淨和緊湊總是值得一提的。 確保清理未使用的資產,刪除不必要的元數據並減少藝術品中的路徑點數量(以及 SVG 代碼)。 (謝謝,傑里米! )
不過,也有一些有用的在線工具可用:
- 使用 Squoosh 以最佳壓縮級別(有損或無損)壓縮、調整大小和處理圖像,
- 使用 Guetzli.it 通過 Guetzli 壓縮和優化 JPEG 圖像,它適用於具有銳利邊緣和純色的圖像(但可能會慢一些)。
- 使用響應式圖像斷點生成器或 Cloudinary 或 Imgix 等服務來自動優化圖像。 此外,在許多情況下,單獨使用
srcset
和sizes
將獲得顯著的好處。 - 要檢查響應式標記的效率,您可以使用imaging-heap,這是一個命令行工具,可以測量視口大小和設備像素比的效率。
- 您可以將自動圖像壓縮添加到您的 GitHub 工作流程中,因此沒有圖像可以在未壓縮的情況下投入生產。 該操作使用可處理 PNG 和 JPG 的 mozjpeg 和 libvips。
- 為了優化存儲,您可以使用 Dropbox 的新 Lepton 格式將 JPEG 平均壓縮 22%。
- 如果您想儘早顯示佔位符圖像,請使用 BlurHash。 BlurHash 拍攝一張圖片,並為您提供一個短字符串(僅 20-30 個字符!),代表該圖片的佔位符。 該字符串足夠短,可以很容易地作為字段添加到 JSON 對像中。
BlurHash 是圖像佔位符的小型緊湊表示。 (大預覽) 有時僅優化圖像並不能解決問題。 為了縮短開始渲染關鍵圖像所需的時間,延遲加載不太重要的圖像並延遲任何腳本在關鍵圖像已經渲染後加載。 最安全的方法是混合延遲加載,當我們使用本機延遲加載和延遲加載時,該庫可以檢測通過用戶交互觸發的任何可見性更改(使用我們稍後將探討的 IntersectionObserver)。 此外:
- 考慮預加載關鍵圖像,這樣瀏覽器就不會太晚發現它們。 對於背景圖片,如果你想更加激進,可以使用
<img src>
將圖片添加為普通圖片,然後將其隱藏在屏幕之外。 - 考慮通過根據媒體查詢指定不同的圖像顯示尺寸來使用 Sizes 屬性交換圖像,例如,操縱
sizes
以交換放大鏡組件中的源。 - 查看圖像下載不一致的情況,以防止前景和背景圖像的意外下載。 注意默認加載但可能永遠不會顯示的圖像——例如在輪播、手風琴和圖像畫廊中。
- 確保始終在圖像上設置
width
和height
。 注意 CSS 中的aspect-ratio
屬性和intrinsicsize
屬性,這將允許我們設置圖像的寬高比和尺寸,因此瀏覽器可以提前預留一個預定義的佈局槽以避免頁面加載期間的佈局跳轉。
現在應該只是幾週或幾個月的事情,縱橫比登陸瀏覽器。 已經在 Safari Technical Preview 118 中。 目前在 Firefox 和 Chrome 中處於領先地位。 (大預覽) 如果您喜歡冒險,您可以使用 Edge 工作程序(基本上是位於 CDN 上的實時過濾器)來切分和重新排列 HTTP/2 流,以通過網絡更快地發送圖像。 邊緣工作者使用 JavaScript 流,這些流使用您可以控制的塊(基本上它們是在 CDN 邊緣上運行的 JavaScript,可以修改流響應),因此您可以控製圖像的交付。
使用服務人員,為時已晚,因為您無法控制線路上的內容,但它確實適用於 Edge 工作者。 因此,您可以在為特定登錄頁面逐步保存的靜態 JPEG 之上使用它們。
成像堆的示例輸出,這是一個命令行工具,用於測量視口大小和設備像素比的效率。 (圖片來源)(大預覽) 還不夠好? 好吧,您還可以使用多背景圖像技術提高圖像的感知性能。 請記住,使用對比度和模糊不必要的細節(或去除顏色)也可以減小文件大小。 啊,你需要放大一張小照片而不損失質量嗎? 考慮使用 Letsenhance.io。
到目前為止,這些優化只涵蓋了基礎知識。 Addy Osmani 發布了一份關於基本圖像優化的非常詳細的指南,該指南非常深入地介紹了圖像壓縮和色彩管理的細節。 例如,您可以模糊圖像的不必要部分(通過對其應用高斯模糊濾鏡)以減小文件大小,最終您甚至可能開始去除顏色或將圖片變為黑白以進一步減小大小. 對於背景圖像,從 Photoshop 中以 0 到 10% 的質量導出照片也是完全可以接受的。
在 Smashing Magazine 上,我們使用後綴
-opt
作為圖像名稱 - 例如,brotli-compression-opt.png
; 每當圖像包含該後綴時,團隊中的每個人都知道該圖像已被優化。啊,不要在網絡上使用 JPEG-XR ——“在 CPU 上解碼 JPEG-XRs 軟件端的處理抵消甚至超過了字節大小節省的潛在積極影響,尤其是在 SPA 的上下文中”(不是與 Cloudinary/Google 的 JPEG XL 混合)。

- 視頻是否正確優化?
到目前為止,我們涵蓋了圖像,但我們避免了關於好的 ol' GIF 的對話。 儘管我們喜歡 GIF,但現在是時候徹底放棄它們了(至少在我們的網站和應用程序中)。 與其加載影響渲染性能和帶寬的繁重的動畫 GIF,不如切換到動畫 WebP(GIF 作為後備)或完全用循環的 HTML5 視頻替換它們。與圖像不同,瀏覽器不會預加載
<video>
內容,但 HTML5 視頻往往比 GIF 更輕更小。 不是一個選項? 好吧,至少我們可以使用有損 GIF、gifsicle 或 giflossy 為 GIF 添加有損壓縮。Colin Bendell 的測試表明,Safari 技術預覽中
img
標籤內的內嵌視頻顯示速度至少比 GIF 快 20 倍,解碼速度快 7 倍,而且文件大小只是一小部分。 但是,其他瀏覽器不支持它。在好消息的土地上,視頻格式多年來一直在大規模發展。 很長一段時間以來,我們一直希望 WebM 能夠成為統治一切的格式,而 WebP(基本上是 WebM 視頻容器內的一張靜止圖像)將成為過時的圖像格式的替代品。 的確,Safari 現在支持 WebP,但儘管 WebP 和 WebM 近來獲得了支持,但這一突破並沒有真正發生。
儘管如此,我們仍然可以將 WebM 用於大多數現代瀏覽器:
<!-- By Houssein Djirdeh. https://web.dev/replace-gifs-with-videos/ --> <!-- A common scenartio: MP4 with a WEBM fallback. --> <video autoplay loop muted playsinline> <source src="my-animation.webm" type="video/webm"> <source src="my-animation.mp4" type="video/mp4"> </video>
但也許我們可以完全重新審視它。 2018 年,開放媒體聯盟發布了一種新的有前途的視頻格式,稱為AV1 。 AV1 具有類似於 H.265 編解碼器(H.264 的演變)的壓縮,但與後者不同的是,AV1 是免費的。 H.265 許可證定價促使瀏覽器供應商改用性能相當的 AV1: AV1(就像 H.265)的壓縮率是 WebM 的兩倍。
AV1 很有可能成為網絡視頻的終極標準。 (圖片來源:Wikimedia.org)(大預覽) 事實上,蘋果目前使用的是 HEIF 格式和 HEVC(H.265),而最新 iOS 上的所有照片和視頻都是以這些格式保存的,而不是 JPEG。 雖然 HEIF 和 HEVC (H.265) 還沒有正確地暴露在網絡上(還沒有?),但 AV1 是——而且它正在獲得瀏覽器的支持。 因此,在您的
<video>
標籤中添加AV1
源代碼是合理的,因為所有瀏覽器供應商似乎都參與其中。目前,最廣泛使用和支持的編碼是 H.264,由 MP4 文件提供,因此在提供文件之前,請確保您的 MP4 使用多通道編碼進行處理,使用 frei0r iirblur 效果(如果適用)進行模糊處理,並且moov atom 元數據被移動到文件的頭部,而您的服務器接受字節服務。 Boris Schapira 為 FFmpeg 提供了精確的指令以最大限度地優化視頻。 當然,提供 WebM 格式作為替代方案也會有所幫助。
需要更快地開始渲染視頻但視頻文件仍然太大? 例如,每當您在登錄頁面上有大型背景視頻時? 一種常用的技術是首先將第一幀顯示為靜止圖像,或者顯示一個經過高度優化的短循環片段,可以解釋為視頻的一部分,然後,只要視頻緩衝足夠,就開始播放實際的視頻。 Doug Sillars 編寫了一份詳細的背景視頻性能指南,在這種情況下可能會有所幫助。 (謝謝,Guy Podjarny! )。
對於上述場景,您可能需要提供響應式海報圖片。 默認情況下,
video
元素只允許一張圖片作為海報,這不一定是最佳的。 我們可以使用 Responsive Video Poster,這是一個 JavaScript 庫,允許您為不同的屏幕使用不同的海報圖像,同時還添加過渡疊加和視頻佔位符的完整樣式控制。研究表明,視頻流質量會影響觀看者的行為。 事實上,如果啟動延遲超過 2 秒左右,觀眾就會開始放棄視頻。 超過這一點,延遲增加 1 秒會導致放棄率增加大約 5.8%。 因此,視頻開始時間的中位數為 12.8 秒也就不足為奇了,其中 40% 的視頻至少有 1 個停頓,20% 的視頻至少有 2 秒的停頓視頻播放。 事實上,在 3G 上,視頻停頓是不可避免的,因為視頻播放速度快於網絡提供的內容。
那麼,解決方案是什麼? 通常小屏幕設備無法處理我們為桌面服務的 720p 和 1080p。 根據 Doug Sillars 的說法,我們可以創建更小的視頻版本,並使用 Javascript 來檢測小屏幕的來源,以確保在這些設備上快速流暢地播放。 或者,我們可以使用流式視頻。 HLS 視頻流將向設備提供適當大小的視頻——抽像出為不同屏幕創建不同視頻的需要。 它還將協商網絡速度,並根據您使用的網絡速度調整視頻比特率。
為了避免帶寬浪費,我們只能為實際可以播放視頻的設備添加視頻源。 或者,我們可以完全從
video
標籤中刪除autoplay
屬性,並使用 JavaScript 為更大的屏幕插入autoplay
。 此外,我們需要在video
上添加preload="none"
來告訴瀏覽器在它真正需要文件之前不要下載任何視頻文件:<!-- Based on Doug Sillars's post. https://dougsillars.com/2020/01/06/hiding-videos-on-the-mbile-web/ --> <video preload="none" playsinline muted loop width="1920" height="1080" poster="poster.jpg"> <source src="video.webm" type="video/webm"> <source src="video.mp4" type="video/mp4"> </video>
然後我們可以專門針對實際支持 AV1 的瀏覽器:
<!-- Based on Doug Sillars's post. https://dougsillars.com/2020/01/06/hiding-videos-on-the-mbile-web/ --> <video preload="none" playsinline muted loop width="1920" height="1080" poster="poster.jpg"> <source src="video.av1.mp4" type="video/mp4; codecs=av01.0.05M.08"> <source src="video.hevc.mp4" type="video/mp4; codecs=hevc"> <source src="video.webm" type="video/webm"> <source src="video.mp4" type="video/mp4"> </video>
然後我們可以在某個閾值(例如 1000 像素)上重新添加
autoplay
:/* By Doug Sillars. https://dougsillars.com/2020/01/06/hiding-videos-on-the-mbile-web/ */ <script> window.onload = addAutoplay(); var videoLocation = document.getElementById("hero-video"); function addAutoplay() { if(window.innerWidth > 1000){ videoLocation.setAttribute("autoplay",""); }; } </script>
按設備和網絡速度劃分的檔數。 速度更快的網絡上的速度更快的設備幾乎沒有停頓。 根據道格·西拉斯的研究。 (大預覽) 視頻播放性能本身就是一個故事,如果您想深入了解它的詳細信息,請查看 Doug Sillars 關於視頻當前狀態和視頻交付最佳實踐的另一個系列,其中包含有關視頻交付指標的詳細信息、視頻預加載、壓縮和流式傳輸。 最後,您可以使用 Stream 或 Not 檢查視頻流的速度或速度。

- 網絡字體交付是否優化?
值得一問的第一個問題是,我們是否可以一開始就使用 UI 系統字體——我們只需要確保仔細檢查它們在各種平台上的顯示是否正確。 如果不是這種情況,我們提供的網絡字體很可能包含字形以及未使用的額外功能和權重。 我們可以要求我們的字體鑄造廠對Web 字體進行子集化,或者如果我們使用開源字體,則使用 Glyphhanger 或 Fontsquirrel 自行對它們進行子集化。 我們甚至可以使用 Peter Muller 的子字體自動化我們的整個工作流程,這是一個命令行工具,可以靜態分析您的頁面以生成最佳的網絡字體子集,然後將它們注入我們的頁面。WOFF2 支持很棒,我們可以使用 WOFF 作為不支持它的瀏覽器的後備——或者也許可以為舊版瀏覽器提供系統字體。 Web 字體加載有很多很多的選項,我們可以從 Zach Leatherman 的“字體加載策略綜合指南”中選擇一種策略(代碼片段也可以作為 Web 字體加載食譜提供)。
今天要考慮的更好的選擇可能是帶有
preload
的關鍵 FOFT 和“妥協”方法。 他們都使用兩階段渲染來分步交付網絡字體——首先是一個小的超子集,需要使用網絡字體快速準確地渲染頁面,然後異步加載系列的其餘部分。 不同之處在於“妥協”技術僅在不支持字體加載事件時才異步加載 polyfill,因此您不需要默認加載 polyfill。 需要快速獲勝嗎? Zach Leatherman 有一個 23 分鐘的快速教程和案例研究,可以讓您的字體井井有條。一般來說,使用
preload
資源提示來預加載字體可能是一個好主意,但在您的標記中包含指向關鍵 CSS 和 JavaScript 的鏈接之後的提示。 對於preload
,存在優先級難題,因此請考慮在外部阻塞腳本之前將rel="preload"
元素注入 DOM。 根據 Andy Davies 的說法,“使用腳本注入的資源在腳本執行之前對瀏覽器是隱藏的,當瀏覽器發現preload
提示時,我們可以使用這種行為來延遲。” 否則,字體加載將在第一次渲染時花費您。當一切都很關鍵時,沒有什麼是關鍵的。 僅預加載每個系列的一種或最多兩種字體。 (圖片來源:Zach Leatherman - 幻燈片 93)(大預覽) 有選擇性並選擇最重要的文件是個好主意,例如那些對渲染至關重要的文件或有助於避免可見和破壞性文本重排的文件。 一般來說,Zach 建議預加載每個系列的一到兩種字體——如果它們不太重要,延遲一些字體加載也是有意義的。
在
@font-face
規則中定義font-family
時,使用local()
值(按名稱引用本地字體)已變得非常普遍:/* Warning! Not a good idea! */ @font-face { font-family: Open Sans; src: local('Open Sans Regular'), local('OpenSans-Regular'), url('opensans.woff2') format ('woff2'), url('opensans.woff') format('woff'); }
這個想法是有道理的:一些流行的開源字體,例如Open Sans,會預裝一些驅動程序或應用程序,所以如果字體在本地可用,瀏覽器不需要下載網絡字體,可以顯示本地立即字體。 正如 Bram Stein 所指出的,“雖然本地字體與網絡字體的名稱匹配,但它很可能不是同一種字體。許多網絡字體與其“桌面”版本不同。文本可能呈現不同,某些字符可能會下降回到其他字體,OpenType 功能可能完全缺失,或者行高可能不同。”
此外,隨著字體隨著時間的推移而演變,本地安裝的版本可能與 Web 字體大不相同,字符看起來也大不相同。 因此,根據 Bram 的說法,最好不要在
@font-face
規則中混合本地安裝的字體和 Web 字體。 除了 Android 對 Roboto 的請求外,Google 字體也對所有用戶的 CSS 結果禁用local()
。沒有人喜歡等待內容顯示。 使用
font-display
CSS 描述符,我們可以控製字體加載行為並使內容能夠立即(使用font-display: optional
)或幾乎立即(超時 3 秒,只要字體被成功下載——使用font-display: swap
)。 (嗯,它比這更複雜一些。)但是,如果您想盡量減少文本重排的影響,我們可以使用字體加載 API (在所有現代瀏覽器中都支持)。 具體來說,這意味著對於每種字體,我們將創建一個
FontFace
對象,然後嘗試獲取所有字體,然後才將它們應用到頁面上。 這樣,我們通過異步加載所有字體來對所有重繪進行分組,然後恰好從備用字體切換到網絡字體一次。 看看 Zach 的解釋,從 32:15 開始,以及代碼片段):/* Load two web fonts using JavaScript */ /* Zach Leatherman: https://noti.st/zachleat/KNaZEg/the-five-whys-of-web-font-loading-performance#sWkN4u4 */ // Remove existing @font-face blocks // Create two let font = new FontFace("Noto Serif", /* ... */); let fontBold = new FontFace("Noto Serif, /* ... */); // Load two fonts let fonts = await Promise.all([ font.load(), fontBold.load() ]) // Group repaints and render both fonts at the same time! fonts.forEach(font => documents.fonts.add(font));
/* Load two web fonts using JavaScript */ /* Zach Leatherman: https://noti.st/zachleat/KNaZEg/the-five-whys-of-web-font-loading-performance#sWkN4u4 */ // Remove existing @font-face blocks // Create two let font = new FontFace("Noto Serif", /* ... */); let fontBold = new FontFace("Noto Serif, /* ... */); // Load two fonts let fonts = await Promise.all([ font.load(), fontBold.load() ]) // Group repaints and render both fonts at the same time! fonts.forEach(font => documents.fonts.add(font));
為了在使用 Font Loading API 的情況下儘早開始獲取字體,Adrian Bece 建議添加一個不間斷空格
nbsp;
在body
的頂部,並使用aria-visibility: hidden
和.hidden
類在視覺上隱藏它:<body class="no-js"> <!-- ... Website content ... --> <div aria-visibility="hidden" class="hidden"> <!-- There is a non-breaking space here --> </div> <script> document.getElementsByTagName("body")[0].classList.remove("no-js"); </script> </body>
<body class="no-js"> <!-- ... Website content ... --> <div aria-visibility="hidden" class="hidden"> <!-- There is a non-breaking space here --> </div> <script> document.getElementsByTagName("body")[0].classList.remove("no-js"); </script> </body>
這與為不同加載狀態聲明不同字體系列的 CSS 一起使用,一旦字體成功加載,更改由 Font Loading API 觸發:
body:not(.wf-merriweather--loaded):not(.no-js) { font-family: [fallback-system-font]; /* Fallback font styles */ } .wf-merriweather--loaded, .no-js { font-family: "[web-font-name]"; /* Webfont styles */ } /* Accessible hiding */ .hidden { position: absolute; overflow: hidden; clip: rect(0 0 0 0); height: 1px; width: 1px; margin: -1px; padding: 0; border: 0; }
body:not(.wf-merriweather--loaded):not(.no-js) { font-family: [fallback-system-font]; /* Fallback font styles */ } .wf-merriweather--loaded, .no-js { font-family: "[web-font-name]"; /* Webfont styles */ } /* Accessible hiding */ .hidden { position: absolute; overflow: hidden; clip: rect(0 0 0 0); height: 1px; width: 1px; margin: -1px; padding: 0; border: 0; }
如果您想知道為什麼儘管進行了所有優化,Lighthouse 仍然建議消除渲染阻塞資源(字體),在同一篇文章中,Adrian Bece 提供了一些讓 Lighthouse 滿意的技術,以及 Gatsby Omni Font Loader,一種高性能異步字體Gatsby 的加載和 Flash Of Unstyled Text (FOUT) 處理插件。
現在,我們中的許多人可能正在使用 CDN 或第三方主機來加載 Web 字體。 一般來說,如果可以的話,自託管所有靜態資產總是更好,因此請考慮使用 google-webfonts-helper,這是一種自託管 Google 字體的輕鬆方式。 如果不可能,您也許可以通過頁面來源代理 Google 字體文件。
值得注意的是,雖然 Google 做了很多開箱即用的工作,所以服務器可能需要一些調整以避免延遲(謝謝,巴里! )
這一點非常重要,尤其是自 Chrome v86(2020 年 10 月發布)以來,由於瀏覽器緩存分區,字體等跨站點資源無法再在同一個 CDN 上共享。 這種行為多年來一直是 Safari 的默認行為。
但是,如果根本不可能,有一種方法可以使用 Harry Roberts 的代碼段獲得最快的 Google 字體:
<!-- By Harry Roberts. https://csswizardry.com/2020/05/the-fastest-google-fonts/ - 1. Preemptively warm up the fonts' origin. - 2. Initiate a high-priority, asynchronous fetch for the CSS file. Works in - most modern browsers. - 3. Initiate a low-priority, asynchronous fetch that gets applied to the page - only after it's arrived. Works in all browsers with JavaScript enabled. - 4. In the unlikely event that a visitor has intentionally disabled - JavaScript, fall back to the original method. The good news is that, - although this is a render-blocking request, it can still make use of the - preconnect which makes it marginally faster than the default. --> <!-- [1] --> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <!-- [2] --> <link rel="preload" as="style" href="$CSS&display=swap" /> <!-- [3] --> <link rel="stylesheet" href="$CSS&display=swap" media="print" onload="this.media='all'" /> <!-- [4] --> <noscript> <link rel="stylesheet" href="$CSS&display=swap" /> </noscript>
Harry 的策略是先發製人地預熱字體的起源。 然後我們為 CSS 文件啟動一個高優先級的異步獲取。 之後,我們啟動一個低優先級的異步獲取,該獲取僅在頁面到達後才應用於頁面(使用打印樣式表技巧)。 最後,如果不支持 JavaScript,我們將退回到原始方法。
啊,說到谷歌字體:你可以通過
&text
只聲明你需要的字符來減少谷歌字體請求的90% 的大小。 另外,最近谷歌字體也增加了對字體顯示的支持,所以我們可以直接使用它。不過要注意一點。 如果您使用
font-display: optional
,那麼使用preload
可能不是最佳選擇,因為它會提前觸發該 Web 字體請求(如果您有其他需要獲取的關鍵路徑資源,則會導致網絡擁塞)。 使用preconnect
來加快跨域字體請求,但要小心preload
,因為從不同來源預加載字體會導致網絡爭用。 所有這些技術都包含在 Zach 的 Web 字體加載配方中。另一方面,如果用戶在可訪問性首選項中啟用了減少運動或選擇了數據保護模式(請參閱
Save-Data
標題),則選擇退出網絡字體(或至少第二階段渲染)可能是個好主意,或者當用戶連接速度較慢時(通過網絡信息 API)。如果用戶選擇了數據保存模式(還有其他用例),我們還可以使用
prefers-reduced-data
CSS 媒體查詢來不定義字體聲明。 如果來自客戶端提示 HTTP 擴展的Save-Data
請求標頭打開/關閉以允許與 CSS 一起使用,則媒體查詢基本上會公開。 目前僅支持在 Chrome 和 Edge 後面的標誌。指標? 要衡量 Web 字體加載性能,請考慮All Text Visible指標(所有字體已加載且所有內容以 Web 字體顯示的時刻)、Time to Real Italics 以及首次渲染後的Web Font Reflow Count 。 顯然,這兩個指標越低,性能越好。
你可能會問,可變字體呢? 重要的是要注意可變字體可能需要重要的性能考慮。 它們為我們提供了更廣泛的印刷選擇設計空間,但它的代價是單個串行請求而不是多個單獨的文件請求。
雖然可變字體大大減少了字體文件的整體組合文件大小,但該單個請求可能會很慢,從而阻止頁面上所有內容的呈現。 所以子集化和將字體分割成字符集仍然很重要。 不過好的一面是,使用可變字體,默認情況下我們會得到一個重排,因此不需要 JavaScript 來對重繪進行分組。
現在,怎樣才能製定防彈的網絡字體加載策略呢? 子集字體並為 2-stage-render 準備它們,使用字體
font-display
描述符聲明它們,使用 Font Loading API 對重繪進行分組並將字體存儲在持久服務工作者的緩存中。 在第一次訪問時,在阻塞外部腳本之前註入腳本的預加載。 如有必要,您可以使用 Bram Stein 的 Font Face Observer。 如果您對測量字體加載的性能感興趣,Andreas Marschke 將探索使用 Font API 和 UserTiming API 進行的性能跟踪。最後,不要忘記包含
unicode-range
以將大字體分解為較小的特定語言字體,並使用 Monica Dinculescu 的字體樣式匹配器來最小化佈局中的不和諧變化,因為後備和網頁字體。或者,要為備用字體模擬 Web 字體,我們可以使用 @font-face 描述符來覆蓋字體指標(演示,在 Chrome 87 中啟用)。 (請注意,調整會因複雜的字體堆棧而變得複雜。)
未來看起來光明嗎? 通過漸進式字體豐富,最終我們可能能夠“在任何給定頁面上僅下載所需的字體部分,並且對於該字體的後續請求,可以根據後續頁面的要求使用額外的字形集動態地‘修補’原始下載意見”,正如 Jason Pamental 解釋的那樣。 增量傳輸演示已經可用,並且正在進行中。
目錄
- 準備:計劃和指標
- 設定切合實際的目標
- 定義環境
- 資產優化
- 構建優化
- 交付優化
- 網絡、HTTP/2、HTTP/3
- 測試和監控
- 快速獲勝
- 一切都在一頁上
- 下載清單(PDF、Apple Pages、MS Word)
- 訂閱我們的電子郵件通訊不要錯過下一個指南。