页面视觉稳定性之优化CLS
最常見的影響CLS的分數的有:
- 未指定尺寸的圖片
- 未指定尺寸的廣告、嵌入元素、iframe
- 動態插入內容
- 自定義字體(引發FOIT/FOUT)
- 在更新DOM之前等待網絡響應的操作
未指定尺寸的圖片
總而言之:在 <img> 和 <video 標簽上始終加上 width 和 height 屬性。或者,使用 CSS aspect ratio boxes 來占據空間。這種方法可以確保在圖片加載過程中,瀏覽器可以分配足夠的空間。
歷史
在web的早期,開發者會給 <img> 標簽加上 width 和 height 屬性,以確保瀏覽器開始獲取圖片之前可以分配好空間,這樣可以減少 reflow 和 re-layout。
<img src="puppy.jpg" width="640" height="360" alt="Puppy with balloons">你也許會注意到這兩個屬性沒有帶單位。這些像素尺寸會確保保留640 * 360的區域。圖片最終會平鋪在這個區域,不管原始尺寸是否一致。
當響應式設計來臨的時候,開發者開始忽略 width 和 height,開始使用css來調整圖片大小。
img {width: 100%; /* or max-width: 100%; */height: auto; }這種方法的缺點是,只有圖片下載的時候,瀏覽器才知道圖片的寬高并且分配好空間。圖片下載完了,每張圖片出現在屏幕上的時候,頁面都會 reflow 一次,會導致頁面頻繁的往下彈。這對于用戶體驗來說非常不友好。
因此而誕生了 aspect ratio。aspect ratio 是圖片的寬高比。比如,x:y的寬高比,指的是寬度x單位,高度y單位。
這也意味著只要我們知道寬高之一,就能計算出另一個屬性。對于一個16:9的寬高比而言:
- 如果圖片有360px的高度,則寬度為 360 x (16 / 9) = 640px
- 如果圖片有640px的寬度,則高度為 640 x (9 / 16) = 360px
現代瀏覽器最佳體驗
現代瀏覽器可以基于 width 和 height 屬性設定默認寬高比,這樣就能避免布局偏移。開發者只需要如下設置:
<!-- set a 640:360 i.e a 16:9 - aspect ratio --> <img src="puppy.jpg" width="640" height="360" alt="Puppy with balloons"> img {aspect-ratio: attr(width) / attr(height); }這樣一來,圖片加載之前,瀏覽器就可以根據寬高屬性分配好空間。圖片加載之后,就可以根據寬度或者高度屬性,按照寬高比來分配實際空間。
圖片的 aspect-ratio 屬性在chrome和firefox上已經可以使用了,safari也快支持了。
如果圖片位于容器內,可以設置寬度為容器寬度,高度為auto,避免高度被固定位360px。
img {height: auto;width: 100%; }響應式圖片
在使用響應式圖片的時候,srcset 定義了圖片可以供瀏覽器選擇的尺寸。為了確保圖片 width 和 height 可以被設置,每張圖片的寬高比必須一致。
<img width="1000" height="1000"src="puppy-1000.jpg"srcset="puppy-1000.jpg 1000w,puppy-2000.jpg 2000w,puppy-3000.jpg 3000w"alt="Puppy with balloons"/>有時候我們希望展示圖片的剪切部分,比如長圖的中間正方形區域,為了視覺好看。
<picture><source media="(max-width: 799px)" srcset="puppy-480w-cropped.jpg"><source media="(min-width: 800px)" srcset="puppy-800w.jpg"><img src="puppy-800w.jpg" alt="Puppy with balloons"> </picture>這樣一來圖片寬高比就不一致了,瀏覽器可能更需要針對每一個資源設置特定寬高比。但目前還沒有好的解決方案,re-layout 依然存在。
未指定尺寸的廣告、嵌入元素、iframe
廣告
廣告是造成布局偏移的罪魁禍首之一。經常性,這些廣告會有動態尺寸,這樣會導致糟糕的用戶體驗,當你在往下瀏覽頁面的時候,廣告突然插入一些可見內容。
在廣告的生命周期里,很多點可以導致布局偏移:
- 廣告容器插入到dom的時候
- 本站代碼調整廣告容器尺寸的時候
- 廣告代碼庫加載的時候(導致容器尺寸改變)
- 廣告內容填充容器的時候(如果最終廣告的尺寸不一樣,導致容器尺寸變化)
好消息是網站可以采用最佳體驗,來減少布局偏移。
- 為廣告位靜態保留空間。
- 換句話說,在廣告代碼庫加載之前,就給容器加好樣式。
- 如果要在內容流中插入廣告,在插入之前確保通過保留尺寸來消除布局偏移。如果這些廣告在屏幕外加載,則沒有這個問題。
- 在視圖頂部插入非粘性廣告的時候要特別注意。
- 避免折疊預留的空間,如果廣告沒有返回,可以在該空間展示占位符。
- 通過預留廣告所需最大尺寸,來避免布局偏移。
- 這很有效,不過如果廣告很小,可能會有大片空白。
- 根據歷史數據,給廣告加上合適的尺寸。
如果廣告不太可能填滿,一些網站會發現在初始的時候折疊廣告位可以減少布局偏移。很難做到每一次都能給廣告位精準的尺寸,除非這個廣告是你自己提供的。
為廣告位靜態保留空間
給廣告容器設置固定的樣式,避免代碼庫加載的時候,重新調整廣告的尺寸。
要額外注意一下小尺寸的廣告,如果預留很大的空間,會導致大片空白。
避免在視圖頂部插入廣告
根據CLS的計算規則,在頂部插入廣告比在中間插入,造成的影響更大。
嵌入元素和iframe
可嵌入的掛件可以允許你在頁面上嵌入web內容(例如,youtube視頻、谷歌地圖、社交媒體的帖子等)。這些嵌入元素可以采用多種形式。
- html fallback,然后js將該fallback轉換成嵌入元素
- 內聯html代碼塊
- iframe嵌入
這些嵌入通常不會事先知道嵌入的大小(例如,社交媒體帖子,是否包含圖片?視頻?或者多行文本?)。結果就是提供嵌入元素的平臺經常無法保證預留足夠的空間,導致布局偏移。
為了應對這種情況,你可以通過提前計算嵌入元素的足夠空間,以最小化CLS。以下工作流可以參考:
- 使用開發者工具檢查最終嵌入的高度
- 一旦嵌入元素加載,iframe容器根據內容重新調整尺寸。
記下尺寸,并相應設置嵌入元素占位符的樣式。你可能還會用到媒體查詢來考慮不同的因素。
動態內容
總而言之,避免在已存在的內容上方插入新內容,除非為了響應用戶交互。這樣可以保證任何布局偏移都是可預期的。
你可能經常會遇到從頂部或者底部彈出的一些內容。這經常發生在banner或者表單的地方,讓頁面的剩余內容產生偏移。
- “注冊即可領取會員大禮包!”
- “最近發表的文章”
- “安裝我們的APP”
- “我們還在接受訂單”
- “GDPR提示,是否允許使用cookie”
如果你需要展示以上的UI內容,請提前預留好空間,避免產生布局偏移。
自定義字體(引發FOIT/FOUT)
下載并渲染自定義字體會引發布局偏移,通過以下兩種方式:
- fallback字體切換到新字體(FOUT - flash of unstyled text)
- 從不可見變成可見,因為新字體的渲染緣故(FOIT - flash of invisible text)
以下工具可以幫你最小化影響:
- font-display 屬性可以讓你修改自定義字體的渲染表現,通過使用可選值:auto, swap, block, fallback 和 optional。不幸的是,除了 optional 之外的屬性都會引發 re-layout,通過以上的其中一種方式。
- Font Loading API 可以減少獲取必要字體的時間。
Chrome 83版本之后,可以采取以下方案:
- 針對關鍵字體使用 <link rel=preload> ,提高優先級,讓字體下載有更高概率趕在fcp之前,這樣就能避免布局偏移。
- <link rel=preload> 和 font-display: optional 結合使用。
動畫
總而言之,優先考慮 transform,而非會影響布局改變的屬性。
在更新DOM之前等待網絡響應的操作
盡可能的在網絡請求時,給一個loading,或者占位符提示,避免用戶在這段時間內進行操作。
開發者工具
可以使用lighthouse和performce檢測CLS。
總結
- 圖片的尺寸,以及其他嵌入元素的尺寸,最開始就設定好,或者預留足夠空間,這樣可以有效避免布局偏移。
- 利用圖片寬高比的屬性,可以在優化CLS的同時,做響應式布局。
- 盡可能不要往已存在內容上方添加新內容。
- web字體盡可能早的加載,避免產生FOIT和FOUT
- 與UI同事配合在交互上避免布局偏移
參考
https://web.dev/optimize-cls/
總結
以上是生活随笔為你收集整理的页面视觉稳定性之优化CLS的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 奥卡姆剃刀
- 下一篇: PCB多层板每层厚度及材质