CLS 累積版面配置偏移是什麼——三個常見來源與一行修法

你正要按下「加入購物車」,手指都快碰到螢幕了,畫面突然往下一頓,一塊廣告卡進那個位置,於是你點到的是廣告,不是商品。這種「畫面自己跳走」的瞬間,每個人都遇過,氣到想關掉分頁。Google 把這種惱人的跳動拿來量化,做成一個分數,這個分數就叫 CLS(Cumulative Layout Shift,累積版面配置偏移)。

很多人以為網站慢只跟載入速度有關,其實版面跳動是另一條獨立的扣分線。一個頁面可以載得很快,CLS 卻很糟;訪客覺得「這網站很頓、很不穩」,多半就是被這些跳動弄的。而 Google 早就把它列進排名訊號,等於你的視覺穩定性會直接寫進搜尋表現裡。

好消息是,中小型網站的 CLS 來源其實很集中,八成跳動都出在三個地方,而且每一個都能用一兩行 CSS 或 HTML 補起來。先把 CLS 是什麼、Google 怎麼打分數講清楚,再一個一個拆這三個常見來源,最後給你一張可以直接對照修的速查表。

CLS 是什麼?為什麼版面跳動會被 Google 盯上

CLS 衡量的是網頁在載入與瀏覽過程中,畫面元素發生「非預期位移」的程度。簡單說,它記錄那些你沒按、沒滑,畫面卻自己動的次數和幅度——文字被往下推、按鈕忽然換位置、圖片載入後把整段內容擠走,這些都算。位移越多、推得越遠,CLS 分數就越高,代表頁面越不穩。

它是 Google 在 2020 年提出的網站體驗核心指標(Core Web Vitals)之一,和另外兩個指標各管一件事。CLS 管視覺穩定性,LCP(Largest Contentful Paint,最大內容繪製)管載入速度,INP(Interaction to Next Paint,與下一次繪製的互動)管互動回應。三個指標都會在 Google 免費的 PageSpeed Insights 工具裡跑出分數,順便附上改善建議。

為什麼 Google 要特別管這件事?因為版面跳動破壞的是最基本的閱讀信任。你正在讀一篇文章,讀到一半元素全部跳開,得重新找剛剛看到哪;你想點某個連結,按下去的瞬間它被推走,結果點到旁邊的東西。這種體驗會讓人煩躁、提高跳出率,而 Google 的排名演算法已經把這類使用者體驗訊號納進評分。它不會把一個爛內容因為 CLS 漂亮就推到第一名,但當兩個頁面內容旗鼓相當、搶同一個關鍵字時,視覺穩定的那個就多一分勝算。

還有一點很多人沒注意:CLS 在手機上往往比電腦明顯。螢幕窄、響應式廣告會隨裝置改變大小,加上行動裝置搜尋本來就是 Google 優先看的對象,所以手機版的版面跳動,對排名的殺傷力通常更大。

Google 怎麼幫 CLS 打分數?0.1 和 0.25 是兩條線

CLS 分數沒有單位,它不是用秒算的,而是把每一次位移的「影響面積」乘上「移動距離」,再全部加總起來。公式是這樣:

位移分數 = 影響面積比例 × 位移距離比例

影響面積比例,是發生位移的元素加上它移動後覆蓋的範圍,占整個可視畫面的比例;位移距離比例,是元素移動的距離占畫面高度的比例。舉個例子,假設一塊廣告突然冒出來,占了半個畫面,把下面的內容往下推了四分之一個畫面的高度,那這次位移的分數就是 0.5 × 0.25 = 0.125。一個頁面從載入到你離開,所有這類位移加起來,就是最終的 CLS。

Google 把分數切成三段,你只要記住兩條界線:

  1. 良好(Good):CLS ≤ 0.1,畫面穩定,這是要達到的目標。
  2. 需要改進(Needs Improvement):0.1 到 0.25 之間,使用者開始注意到頁面在動。
  3. 較差(Poor):> 0.25,跳動嚴重,體驗明顯受損。

這裡有個常被忽略的細節。Google 判定「通過」與否,看的不是你自己測一次的數字,而是真實使用者資料——至少要有 75% 的頁面瀏覽落在「良好」這一段,才算過關。所以你在 PageSpeed Insights 上會看到兩種 CLS——上面「實際使用者體驗」那塊是真實流量累積的(來自 Chrome 使用者體驗報告,也就是 CrUX),下面「診斷效能問題」那塊是工具當下載入測出來的。兩個數字常常對不上,因為實驗室工具通常只測網頁初始載入,而真實資料涵蓋整個瀏覽過程,連你往下滑、延遲載入的內容跳一下都算進去。

順帶一提,並不是所有跳動都會被算。使用者互動後 500 毫秒內發生的位移,會被當成「預期中的變化」排除掉——你按了「展開」按鈕,內容跟著長出來,這不扣分。真正扣分的,是你什麼都沒做,畫面卻自己動的那種。

廣告為什麼是跳動最大的元兇?預留版位就能壓下來

在所有 CLS 來源裡,廣告是殺傷力最大的一個,尤其對靠廣告收益的中小型網站。問題出在動態廣告的大小是浮動的。廣告聯播網為了讓出價競爭更激烈、點擊率更高,常常支援多種尺寸,到底放多大要等競價結果出來才知道。於是頁面先載好了,廣告才姍姍來遲,一插進來就把下面整段內容往下擠,CLS 瞬間飆高。

修法的核心觀念只有一個,就是「先把位置留好,再讓廣告進來」。你不能等廣告決定大小,而是反過來,先給它一個固定的容器,廣告無論大小都塞進這個框,框外的內容就不會被推動。最直接的一行 CSS 是替廣告容器設一個最小高度:

.ad-slot { min-height: 280px; }

這樣即使廣告還沒載入,那 280 像素的空間已經被卡住,內容不會往上補位、之後又被擠開。如果你的廣告在不同裝置會用不同尺寸,可以搭配媒體查詢,給手機和電腦各設一個合理的 min-height

這裡有個現實的取捨。有些網站發現某個版位(特別是頁首那種又大又顯眼的)怎麼留都還是會跳,因為它的尺寸落差太大。這時與其硬撐,不如重新檢視這個版位值不值得——把影響最大的廣告版位關掉,或在手機版隱藏,往往是壓低行動裝置 CLS 最有效的一招。廣告收益是一回事,但如果版位太吵導致整站體驗變差、連帶拖累搜尋排名,沒了流量,再多版位也換不到收入。這個帳要連著算。

字型載入為什麼讓文字跳一下?一行 font-display 解決

你有沒有看過這種狀況。頁面打開的瞬間,文字先用一種字體閃了一下,過半秒才換成網站真正要用的字型,換的同時整段文字的行高、寬度微微一變,後面的內容跟著抖一下。這就是網頁字型造成的版面跳動。

原因在於自訂字型要從伺服器下載,下載需要時間。在字型到位之前,瀏覽器有兩種選擇。要嘛先用備用字型把文字撐出來(之後換成正式字型時版面重排,叫做 FOUT,無樣式文字閃爍),要嘛乾脆讓文字先隱形、等字型載好才顯示(叫做 FOIT,隱形文字閃爍)。不管哪一種,只要備用字型和正式字型的字寬、字高不一樣,換的那一下就會推動周圍內容,產生 CLS。

最簡單的一行修法,是在 @font-face 裡指定字型的顯示行為:

@font-face { font-family: 'YourFont'; font-display: swap; src: url(...); }

swap 會讓瀏覽器先用備用字型把文字顯示出來,等正式字型載好再換,避免文字隱形那段空白。不過要注意,swap 解決的是「看不到字」的問題,換字型那一刻的重排還是可能發生。如果你想連那一下抖動都壓掉,有兩條進階路可走:

第一,把備用字型挑得跟正式字型「長得像」,並在 font-family 裡寫清楚備用字型,例如 font-family: 'YourFont', sans-serif;,這樣字寬落差小,換的時候幾乎看不出來。第二,用 size-adjustascent-override 這些 CSS 描述子,去微調備用字型的尺寸,讓它和正式字型對齊。如果某個字型是首屏關鍵,也可以用 <link rel="preload"> 提早把它載進來,讓它趕得上第一次繪製,根本不必換。

圖片沒設尺寸為什麼最常見?補上 width 和 height 就好

圖片沒指定尺寸,是 CLS 裡最普遍、也最容易修的一個來源。狀況是這樣。瀏覽器在解析 HTML 時,如果圖片標籤沒寫尺寸,它根本不知道這張圖會占多大空間,只好先給它 0 像素的位置,繼續往下排文字。等圖片真的下載完、量出實際大小,瀏覽器才回頭把空間撐開,於是底下的文字被整個往下推——你正在讀的那一段,突然就跳走了。

修法簡單到有點不可思議,就是在 <img> 標籤裡把寬高補回去:

<img src="photo.jpg" width="640" height="360" alt="商品照片">

這裡的 widthheight 不用加單位。瀏覽器拿到這兩個數字,會先算出圖片的長寬比(640:360 等於 16:9),在圖片還沒載入時就照這個比例把空間預留好。圖片到了直接填進去,不推動任何東西。要強調的是,現代瀏覽器只要看到 widthheight,就會自動換算 aspect-ratio,所以你不必擔心圖片在響應式版面被拉變形——只要在 CSS 補一句 img { height: auto; },圖片就會依容器寬度等比例縮放,比例不變。

如果你的圖片是放在容器裡、寬度跟著螢幕變,純用 CSS 的 aspect-ratio 也能達到同樣效果:

.img-box { aspect-ratio: 16 / 9; width: 100%; }

容器先照 16:9 把高度占好,圖片載入前後都是同一個高度,自然不會跳。這招對影片嵌入、iframe、地圖這類「載入前不知道多大」的內容一樣管用——只要事先把比例或高度框住,它們就沒辦法在載入時推動版面。

三個常見來源與修法的對照速查表

把上面三個中小站最常踩的 CLS 來源,連同對應的一行修法整理成一張表,你可以直接拿去對照自己的網站:

跳動來源(問題行為)為什麼會跳一行修法
廣告版位沒留空間動態廣告大小要等競價才決定,載入後插進來把內容往下擠.ad-slot { min-height: 280px; } 先把版位框住
網頁字型載入後重排備用字型和正式字型尺寸不同,換字型時推動周圍文字@font-face { font-display: swap; },並指定相近的備用字型
圖片沒寫尺寸瀏覽器不知道圖多大,先給 0 空間,載入後才撐開推走文字<img width="640" height="360">,或 CSS aspect-ratio: 16 / 9;
影片、iframe、嵌入內容沒設尺寸同圖片,載入前不知道大小容器設 aspect-ratiomin-height 預留空間
延遲載入內容沒占位內容滑到才載入,沒留位置就把下方頂開容器先給 min-height,沒內容也別讓它收合

這張表抓的是「先預留、再載入」這個共同邏輯。不管是廣告、字型、圖片還是任何延遲出現的東西,CLS 的解法都繞著同一句話——在它出現之前,就把它要占的空間先卡好。瀏覽器只要在排版的第一時間就知道每塊內容多大,就不必載到一半回頭重排,畫面自然穩。

改完怎麼確認?用 PageSpeed Insights 對症下藥

修完之後別憑感覺,拿 PageSpeed Insights 跑一次最實在。輸入網址,它會在「診斷效能問題」裡列出「避免大量版面配置位移」這條稽核,點開能看到具體是哪幾個元素在跳、各自貢獻多少分數。順著它指的元素去補尺寸、留空間,再跑一次,分數掉到綠色的 0.1 以下就算過了。

如果你想看得更細,Chrome 開發者工具的「效能」面板可以一邊操作頁面、一邊即時監看 CLS,特別適合抓那種「滑到一半才跳」的載入後位移——這種跳動實驗室工具初次載入往往測不到,得真的去滑、去互動才會現形。

要提醒的是,真實使用者的 CLS 資料需要時間累積。你今天修好,PageSpeed 上面那塊「實際使用者體驗」的數字不會馬上變,因為它是過去 28 天真實流量的彙整,得等新資料慢慢覆蓋舊的。所以改完先看「診斷」那塊的即時分數確認方向對了,剩下的交給時間,數字會自己跟上來。

回到開頭那個快點到「加入購物車」、卻被廣告卡走的瞬間——你要做的,就是別讓自己的訪客也經歷那一下。版面穩定不是什麼高深技術,而是一種對訪客的體貼。把廣告的位置留好、把字型和圖片的尺寸標清楚,這三件事做完,大部分的跳動就消失了,分數會還你一個漂亮的綠色。