什麼是佈局偏移#
一個十幾秒的短視頻解釋清楚。
更詳細的解釋是:佈局偏移指的是在網頁上發生突然變化時,頁面中的內容位置發生意外移動的現象。這種情況常常讓人感到困擾,因為它會導致閱讀中斷或誤操作。佈局偏移通常是由於資源異步加載或動態添加到頁面上的 DOM 元素導致的。可能的原因包括具有未知尺寸的圖像或視頻、字體與其備用字體渲染大小不同,或者第三方廣告或小部件動態調整大小。
難受的是,網站在開發過程中的功能通常與用戶體驗有很大不同。個性化或第三方內容在開發中的行為通常與生產環境中不同,測試圖像通常已經存在於開發者的瀏覽器快取中,本地運行的 API 調用通常非常快,延遲幾乎不可察覺。
什麼是 CLS#
累積佈局偏移 CLS(Cumulative Layout Shift)是一個指標。
是對頁面整個生命週期中發生的每個意外佈局變化的最大佈局變化分數的度量。
CLS 通過測量實際用戶遇到佈局偏移的頻率來幫助解決佈局偏移問題。它可以幫助開發者了解佈局偏移在真實用戶中發生的情況,從而採取相應的措施進行修復。
為什麼要優化 CLS#
佈局偏移是一個非常影響用戶體驗的問題,通過上面那一個簡短的視頻也能理解。
佈局偏移通常會導致意外點擊、頁面方向的迷失,最終導致用戶受挫。用戶往往不會逗留太久。有時也會使用戶不按照預期的產品流程走。
通常優化好佈局偏移能夠很好地提高用戶黏性、用戶停留時間等指標。
Yahoo! JAPAN News 通過降低 CLS 0.2 分,得到如下成果。
如何降低 CLS#
圖片等媒體元素佔位#
在圖像、視頻等媒體資源元素中始終包含寬度和高度大小屬性。或用 CSS 中的 min-height
、aspect-ratio
或類似的方式保留所需的空間。
aspect-ratio
#
可以用來直接指定當前元素的比例。
https://developer.mozilla.org/zh-CN/docs/Web/CSS/aspect-ratio
對瀏覽器的支持:
padding-bottom
#
如果考慮瀏覽器支持問題仍然可以考慮使用目前一個被廣泛接受的基解決方案 "Padding-Top Hack"。這個解決方案需要一個父元素和一個絕對型的子元素。然後計算出長寬比的百分比來設置為 padding-top
。例如:
<div class="container">
<img class="media" src="..." alt="...">
</div>
.container {
position: relative;
width: 100%;
padding-top: 56.25%; /* 16:9 Aspect Ratio */
}
.media {
position: absolute;
top: 0;
}
使用不易產生偏移的 CSS#
其中 transfrom
表現很好,以下舉幾個例子。
用例可以在這裡找到:https://play.tailwindcss.com/26PxFA6UVI
zoom
VS transform: scale
#
當 zoom
會撐大頁面並向右偏移時,transform: scale
只是在原地放大。
margin
VS transform: translate
#
margin
造成父元素變大,transform: translate
只是讓當前元素移動。
border
VS box-shadow
#
border
會撐起父元素,而 box-shadow
並不會。
小心你的懶加載#
懶加載會引起佈局的偏移,如果你在有懶加載長列表的裡進行跳轉,請小心!
無動畫進行跳轉,能夠一定程度上避免該問題。
小心使用 transition: all
#
在頁面首次加載或者跳轉頁面時,transition: all
可能會導致元素的 padding
等從參數為 0 開始渲染,造成頁面的抖動。
這都是痛:
Commit:表格以及友情連結圖標抖動
Commit:修復導航欄抖動問題
標籤順序導致的偏移問題#
由於在移動端上優先展示主要內容,因此側邊欄的 markup 位於主要內容的後面;而在更大的螢幕上,則通過設置 CSS order
的方式進行排序,將主要內容移到中間(即第二列),偽代碼如下:
export default function MainLayout(props) {
return (
<Container>
<Main className={css`@media screen and (min-width: breakpoint) { order: 0 }`} />
<Left className={css`@media screen and (min-width: breakpoint) { order: -1 }`} />
<Right className={css`@media screen and (min-width: breakpoint) { order: 1 }`} />
</Container>
)
}
瀏覽器在首次繪製時並沒有完整解析 DOM、只知道 <Main />
的存在、但不知道 <Left />
或者 <Right />
的存在,才因此將 <Main />
渲染進第一列而不是第二列;直到第二次繪製時,瀏覽器才將 <Main />
渲染進第二列、將 <Left />
渲染進第一列。
Chrome 並不是一次完整解析 HTML 的,在以下兩種情況下,Chrome 會暫停解析、開始渲染和繪製:
- Chrome 解析器在讀取了 65535 字節的 HTML 後暫停
- Chrome 在遇到
<script>
標籤後,會繼續讀取約 50 個「Token」之後暫停
詳細了解請看:優化博客的累積佈局偏移(CLS)問題
網頁跳轉與前進後退快取#
默認情況下,所有瀏覽器都使用 bfcache,但由於各種原因,有些站點不適合使用 bfcache。有關如何測試和識別阻止 bfcache 使用的任何問題的更多詳細信息,請閱讀 bfcache 文章。
在你離開後,bfcache 將頁面保存在瀏覽器內存中很短的一段時間,所以如果你返回它們,那麼它們將完全恢復為你離開時的樣子。這意味著完全加載的頁面立即可用,而不會出現任何變化。
現在的 SPA 應用也能很輕易的保證路由跳轉頁面佈局的一致性。記住始終保持你的目錄和導航欄在頁面的固定位置。
字體#
在下載和渲染網絡字體之前,通常有兩種處理方式:
- 使用網絡字體替代備用字體(FOUT—— 未樣式化文本的閃爍)。
- 使用備用字體顯示 “不可見” 文本,直到網絡字體可用並且文本可見(FOIT—— 不可見文本的閃爍)。
這兩種方式都可能導致佈局變化。即使文本是不可見的,它仍然使用備用字體進行佈局。這意味著使用該字體的文本塊以及周圍的內容在網絡字體加載時會發生佈局變化,與 FOUT 的可見字體完全相同。
以下方法可以幫助你最小化這種問題:
- 使用
font-display: optional
可以避免重新佈局,因為只有在初始佈局時網絡字體可用時才會使用它。 - 使用匹配度高的備用字體。例如,使用
font-family: "Google Sans", sans-serif;
將確保在加載 "Google Sans" 字體時使用瀏覽器的無襯線備用字體。如果只使用font-family: "Google Sans"
而不指定備用字體,將使用默認字體,而在 Chrome 上默認字體是 "Times",它是比默認無襯線字體的匹配度更差。 - 使用新的
size-adjust
、ascent-override
、descent-override
和line-gap-override
API 來儘量減小備用字體和網絡字體之間的大小差異,詳細信息請參閱 “Improved font fallbacks” 文章。 - 使用 Font Loading API 可以減少獲取所需字體的時間。
- 使用
<link rel=preload>
儘早加載關鍵的網絡字體。預加載的字體有更高的機會達到首次繪製,這樣就不會發生佈局變化。 - 閱讀有關字體最佳實踐的 “Best practices for fonts” 文章。
使用真正的骨架屏#
測量 CLS 分數#
生產階段#
- Chrome User Experience Report
- PageSpeed Insights
- Search Console (Core Web Vitals report)
- web-vitals JavaScript library
實驗階段#
Lighthouse in DevTools#
能夠針對移動設備和桌面設備生成網頁的實際性能報告,並能夠提供關於如何改進相應網頁的建議。
在本地開發期間從 DevTools 運行 Lighthouse 非常方便。
PageSpeed Insights#
應該就是在線版的 Lighthouse。
Performance in DevTools#
性能選項卡在 Chrome 的 DevTools 配置文件的所有頁面行為在一段時間內記錄。時間軸上會出現一個標記為 “Experience” 的圖層,突出顯示佈局的變化和發生變化的元素。
Web Vitals extension#
最好將 Web vital 擴展視為查找性能問題的抽查工具,而不是全面的調試工具 —— 這是 Chrome 的 DevTools 中的性能選項卡的工作。
結語#
作為一個對自己項目有較高要求的人,平常幾乎都會接觸到佈局偏移優化或者 Lighthouse,只不過之前自己瞎折騰的時候還沒有 CLS 這個概念,現在算是對 CLS 有了較為清晰的概念了。
CLS 作為一個非常基礎的優化指標,在用戶體驗上非常重要,任何項目都應該針對 CLS 做優化。
如有勘誤,請及時指出,感謝!
參考#
- https://web.dev/cls/
- https://web.dev/optimize-cls
- https://developers.google.com/publisher-tag/guides/minimize-layout-shift
- https://web.dev/yahoo-japan-news/
- https://addyosmani.com/blog/infinite-scroll-without-layout-shifts/
- https://blog.skk.moe/post/fix-blog-cls/
- https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio