Core Web Vitals 效能優化專家
建立摘要
CLS 優化 LCP CLS INP

CLS 圖片修復:用 width、height 和 aspect-ratio 把版面跳動從 0.24 壓到 0.04

圖片是 CLS 最常見的元兇。本文說明瀏覽器如何用 width/height 屬性推算 aspect-ratio 預留空間,以及 CSS width: auto 陷阱、圖片容器設計問題和 Next.js fill 模式的正確修法。含修復前後實測數據。

陳

陳柏翰

網站效能工程師

12 分鐘閱讀
CLS 圖片修復:用 width、height 和 aspect-ratio 把版面跳動從 0.24 壓到 0.04

圖片為什麼是 CLS 最大的元兇之一

把一個電商列表頁的 CLS 從 0.24 壓到 0.04,我只修了一件事:補上所有 img 標籤的 width 和 height 屬性。23 張圖片,花了不到兩小時。圖片(img 元素)是網頁上 CLS 分數高的最普遍原因之一,而且幾乎所有情況都有直接的修法。

根本原因在於瀏覽器的 HTML parsing 流程。瀏覽器在 HTML 解析階段就要決定每個元素的佔位空間,但圖片是非同步載入的,瀏覽器在下載完圖片之前根本不知道它的尺寸。如果你沒有提供任何尺寸提示,瀏覽器會先給圖片分配 0×0 的空間,等圖片載入完成後,才把圖片撐到真實尺寸,把下方所有內容往下推。這個「先 0×0,載入後才展開」的行為就是 CLS 的主要觸發機制。

2025 年的 Web Almanac 數據顯示,仍有 62% 的行動裝置頁面包含至少一張無明確尺寸的圖片。這個問題很基礎,但解決率很低,代表修好它就能有效拉開差距。

哪些圖片最容易造成高 CLS

不是所有圖片的 CLS 貢獻都相等。視口上方的圖片(Hero image、文章封面圖、列表頁產品圖),如果在頁面初始渲染後才載入,CLS 值會特別高,因為 impact fraction(位移影響的視口面積)更大。視口底部的圖片即使沒有尺寸,使用者通常還沒捲到那裡,位移發生時視口內可能沒有任何元素,CLS 貢獻接近零。

診斷優先順序:先用 Chrome DevTools Performance 面板找出 Layout Shift 記錄,確認是哪些 img 元素的 impact fraction 最高,從那裡開始修。

最快的修法:在 img 標籤補上 width 和 height 屬性

在 img 標籤上明確寫出 width 和 height 屬性,是防止圖片 CLS 最直接的方法。現代瀏覽器(Chrome 79+、Firefox 71+、Safari 14+)會把這兩個屬性的值自動換算成 CSS aspect-ratio,在圖片下載前就預留正確的空間。

<!-- 之前:沒有尺寸,瀏覽器給 0×0 佔位 -->
<img src="product.jpg" alt="產品圖">

<!-- 之後:瀏覽器推算 aspect-ratio = 800/600 = 4/3,預留高度 -->
<img src="product.jpg" alt="產品圖" width="800" height="600">

這裡的 width="800" height="600" 是圖片的原始尺寸(intrinsic size),不是你希望它在頁面上顯示的尺寸。瀏覽器會用這個比例預留空間,CSS 再決定實際渲染寬度。如果你的 CSS 是 img { width: 100%; height: auto; },圖片寬度會填滿容器,但高度會按 4:3 的比例正確計算,不會跳動。

loading="lazy" 圖片也一樣要設尺寸

有個常見的誤解是「lazy loading 的圖片不在視口內,所以不影響 CLS」。這在技術上只對一半。loading="lazy" 讓瀏覽器延遲下載,但如果那張圖片位於頁面上方,使用者捲動到它的位置時,圖片才開始載入,載入完成後一樣會把內容往下推。這種延遲的 CLS 同樣計入分數。

規則很簡單:所有 img 都需要 width 和 height,無論有沒有 lazy loading。

CSS aspect-ratio:響應式圖片的完整做法

如果你的圖片是響應式的(width: 100%,隨容器縮放),HTML 的 width 和 height 屬性加上正確的 CSS 就能完整解決問題。但如果你用的是 CSS-in-JS、Tailwind、或需要手動控制 aspect-ratio,可以直接用 CSS aspect-ratio 屬性:

/* 方法 1:HTML 屬性 + CSS 讓它響應式 */
img {
  width: 100%;
  height: auto; /* 配合 HTML 屬性推算出的 aspect-ratio 自動計算高度 */
}

/* 方法 2:純 CSS 設定 aspect-ratio(不依賴 HTML 屬性)*/
.product-image {
  width: 100%;
  aspect-ratio: 4 / 3; /* 直接設定比例 */
}

/* 方法 3:已知圖片固定顯示尺寸 */
.thumbnail {
  width: 200px;
  aspect-ratio: 1 / 1; /* 正方形縮圖 */
}

CSS aspect-ratio 的優點是你不需要知道圖片的精確原始尺寸,只要知道比例就夠了。特別適合 CMS 驅動的網站,因為編輯上傳的圖片尺寸不統一,但可以強制統一顯示比例。

srcset 和 sizes 不影響 CLS 防護

使用 srcset 提供多個解析度的圖片,不會干擾 CLS 防護機制。瀏覽器在決定用哪個 srcset 版本之前,就已經用 width/height 屬性計算好 aspect-ratio 並預留空間了。srcset 只影響「下載哪個版本的圖片」,不影響「提前預留多少空間」。

<img
  src="product-800.jpg"
  srcset="product-400.jpg 400w, product-800.jpg 800w, product-1600.jpg 1600w"
  sizes="(max-width: 768px) 100vw, 50vw"
  width="800"
  height="600"
  alt="產品圖"
  loading="lazy"
>

這個寫法:width/height 讓瀏覽器預留空間,srcset/sizes 讓瀏覽器選最適合的圖片解析度,兩件事獨立運作,互不干擾。

width: auto 會讓你的修法全部失效

這是修完之後 CLS 還是沒改善的最常見原因,也是效能工程師最容易踩到的坑之一。如果你的 CSS 裡有這樣的規則,HTML 屬性的修改就會完全失效:

/* CSS reset 或框架預設樣式 */
img {
  width: auto;
  height: auto;
}

/* 或是這樣 */
* {
  width: auto;
}

width: auto 會覆蓋掉 HTML 屬性推算出的 aspect-ratio。機制是這樣的:瀏覽器用你寫的 width="800" height="600" 計算出 aspect-ratio: 800/600,但是當 CSS 把 width 設成 auto 時,這個推算的 aspect-ratio 就失效了,瀏覽器不再能預先計算圖片要佔多少垂直空間。結果圖片在下載完成前仍然是 0×0,一樣會造成版面跳動。

修法:把 height: auto 留著(它讓高度跟著 aspect-ratio 自動計算),但把 width 改成明確值或百分比:

/* 正確:width 明確,height 讓瀏覽器用 aspect-ratio 計算 */
img {
  width: 100%;  /* 或固定 px 值 */
  height: auto;
}

/* 錯誤:width: auto 破壞 aspect-ratio 推算 */
img {
  width: auto;
  height: auto;
}

檢查方式:打開 DevTools > Elements > Computed 分頁,看 aspect-ratio 這個屬性的值。如果顯示 auto(不是你寫的比例),代表 HTML 屬性推算失敗了。

圖片容器設計不對,修了也沒用

圖片本身設好了 width 和 height,但外層容器的尺寸是動態的,這種情況下圖片本身不會跳動,但容器本身的大小改變仍然會造成 CLS。

圖片容器未設定尺寸時的 CLS 觸發路徑,對比正確設定容器後的穩定版面
左:容器無固定尺寸,圖片載入後撐開容器觸發 CLS;右:容器設定 aspect-ratio,空間預先保留

典型場景:你用一個 div.card 包著圖片,.card 沒有設定高度,完全由內容撐開。即使圖片有 width="400" height="300",在圖片下載前,.card 的高度是 0(或只有 padding/margin),圖片載入後 .card 突然從 0 變成 300px,下方的卡片全部往下推,這就是 CLS。

正確的容器設計:

/* 方法 1:容器用 aspect-ratio 預留空間 */
.card-image-wrapper {
  width: 100%;
  aspect-ratio: 4 / 3;
  overflow: hidden;
}

.card-image-wrapper img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* 方法 2:padding-top hack(兼容舊瀏覽器)*/
.card-image-wrapper {
  position: relative;
  padding-top: 75%; /* 4:3 比例 = 3/4 × 100% */
  overflow: hidden;
}

.card-image-wrapper img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

注意:overflow: hidden 本身不會造成 CLS,但它通常和未設尺寸的容器搭配使用。修 CLS 的重點不是移除 overflow: hidden,而是讓容器在圖片載入前就佔據正確的空間。

Next.js / React 框架的圖片 CLS

Next.js 的 next/image 元件預設就處理了大部分的圖片 CLS 問題。它強制你提供 width 和 height(或使用 fill 模式),並自動產生正確的 HTML 屬性,不讓你犯這個錯誤。

// 正確:明確提供尺寸
import Image from 'next/image'

<Image
  src="/product.jpg"
  alt="產品圖"
  width={800}
  height={600}
  priority={false}  // LCP 圖片設 priority={true}
/>

但 fill 模式有一個常被忽略的 CLS 陷阱:

// 錯誤:fill 模式但容器沒有設定尺寸
<div>
  <Image src="/banner.jpg" alt="橫幅" fill />
</div>

// 正確:fill 模式配合固定尺寸容器
<div style={{ position: 'relative', width: '100%', height: '400px' }}>
  <Image
    src="/banner.jpg"
    alt="橫幅"
    fill
    style={{ objectFit: 'cover' }}
  />
</div>

使用 fill 模式時,next/image 不知道圖片的 intrinsic size,它依賴容器的尺寸來確定大小。如果容器沒有設定固定高度(只是 height: auto 或沒設),圖片載入後容器會從 0 撐開,一樣觸發 CLS。

在 React(非 Next.js)專案中,你需要手動加上 width/height 屬性,或使用自訂的圖片包裝元件來強制設定容器尺寸。圖片格式與壓縮優化可以進一步降低圖片載入時間,間接減少 CLS 觸發窗口。

用 Chrome DevTools 找出造成 CLS 的圖片

修之前先診斷哪張圖片的 CLS 貢獻最大,效率更高。用 Chrome DevTools Performance 面板做這件事:

  1. 開 DevTools(F12)> Performance 分頁
  2. 點「Record」(圓形按鈕)> 重新載入頁面 > 停止錄製
  3. 在時間軸上找紅色的 Layout Shift 標記(通常在頁面載入後 1-3 秒內)
  4. 點選 Layout Shift 事件 > 下方 Summary 分頁看「Had recent input」(true = 使用者觸發,不計入 CLS)
  5. 展開 Layout Shift 的節點,找 Sources 欄位,列出了哪些元素移動了
  6. 找到 img 元素後,看它的 impact fraction(越高 = CLS 貢獻越大)

也可以用 web.dev 的 CLS 指南 提到的 PerformanceObserver API 在開發環境中直接 log 出每個 Layout Shift 事件的來源元素:

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (!entry.hadRecentInput) {
      console.log('Layout Shift:', entry.value, entry.sources);
    }
  }
});
observer.observe({ type: 'layout-shift', buffered: true });

找出 impact fraction 最高的 img 元素,從那裡優先修。通常修前三名就能把 CLS 降到 0.1 以下。也可以同時搭配 LCP 元素識別的方法,確認圖片修復不會影響 LCP 元素的優先載入。

修完圖片 CLS 能降多少

把一個電商列表頁的所有產品圖(共 23 張)從無明確尺寸改成有 width/height 屬性並修正容器設計,PageSpeed Insights 和 CrUX Field Data 的變化如下:

指標 修復前 修復後 改善量
CLS(Lab,Lighthouse) 0.24 0.04 -83%
CLS(Field,CrUX P75) 0.18 0.06 -67%
PageSpeed Insights 分數(手機) 58 74 +16
修復工時 約 1.5 小時(含 HTML 修改 + 驗證)
圖片 CLS 修復前後數據對比:CLS 從 0.24 降至 0.04,PageSpeed 分數從 58 升至 74
電商列表頁修復前後對比,Field Data(CrUX)和 Lab Data(Lighthouse)均顯著改善

CLS 剩下的 0.04 來自廣告插入,那是另一個問題,屬於動態內容 CLS,不是圖片尺寸問題。圖片修復能解決的部分,通常能做到 0.05 以下。如果修完還是有剩餘 CLS,用上面的 DevTools 方法找其他來源。

關於如何判斷這些數字對排名的實際影響,Core Web Vitals 完整指南有詳細說明 Google 的評估標準。你也可以從 CLS 計算方式理解 impact fraction 和 distance fraction 的加權機制,會更清楚哪些圖片最值得優先修。

圖片 CLS 修復 Checklist

圖片造成的 CLS 絕大多數都能用以下七個步驟系統性修完。每個步驟是獨立的修復點,依序確認,從上到下,遇到問題就修完再往下:

  1. 所有 img 元素有 width 和 height 屬性:填入圖片的原始尺寸(intrinsic size)
  2. CSS 沒有 width: auto:確認 CSS 不會覆蓋 HTML 屬性推算的 aspect-ratio
  3. 響應式圖片:img { width: 100%; height: auto; }:讓高度按 aspect-ratio 自動計算
  4. 圖片容器有明確尺寸:用 aspect-ratio CSS 屬性,或 padding-top hack 讓容器預留空間
  5. Next.js:fill 模式配合固定尺寸容器:容器必須設 position: relative 和明確高度
  6. lazy loading 圖片同樣設 width/height:loading="lazy" 不能省略尺寸屬性
  7. 用 DevTools 驗證:Performance 面板確認沒有 Layout Shift 事件,或剩餘 CLS < 0.1

圖片 CLS 是所有 CLS 修復中 CP 值最高的,因為改動小、效果立竿見影。修完後如果 CLS 還超過 0.1,再去看廣告動態插入、字體 FOUT 或 CSS 動畫的部分,那些是 Google 官方文件列出的其他 CLS 來源。記得同時確認 LCP 圖片的 fetchpriority 設定,圖片 CLS 修好後,LCP 通常也會跟著改善。

陳

陳柏翰

網站效能工程師

專注前端效能優化超過 8 年,曾協助電商、媒體和 SaaS 平台改善 Core Web Vitals 指標。相信效能優化應該基於數據而非直覺。

50+ 效能實驗
8 年 前端經驗
200+ 優化案例

想看更多?

探索所有效能實驗

查看完整的實驗報告列表,找到你需要的效能優化方案。

所有實驗報告 免費效能診斷