All You Need To Know About Optimal Images

所有最佳化網頁圖片的技巧都寫在這了!

前言

大多數網站中圖片與影片是傳輸資料中最繁重的存在,了解如何改善它們是最划算的選擇

如果網站效能或加載速度出現問題,第一步可以從圖片或影片開始改善。讓我們先從簡單的例子一步一步找出潛在的問題以及如何解決,了解圖片實際上有很多有趣的細節可以調整。

良好圖片的通則

以下是一些為網站提供好圖片的通則,偏向大方向理念而非語法撰寫上的建議。

合適的尺寸

如果在小畫面上呈現一張超大的圖片,再怎麼精細的影像也無法呈現清楚圖片的細節(除非放大來看),因此只要畫面能夠顯示多大張的圖片就提供多大尺寸的圖片即能達成節省運算與傳輸資源的目的。

設計師不一定會留意這一點,身為前端工程師需要與設計者溝通,讓對方提供合適的圖片供網頁使用,工程師也可以學習基本的繪圖軟體輸出操作,要多大的素材自行輸出,或是上網找圖片壓縮與裁切工具。

Figma 與 Adobe XD 的輸出介面
Figma 與 Adobe XD 都可以選擇畫布的輸出大小與格式

不同的「裝置尺寸 Device Resolution」與「像素密度 Pixel Density」下合適的圖片大小也會有所不同

合適的格式

不同的圖片格式有不同的特性與局限,先思考好要使用什麼方式傳輸圖像,是向量還是點陣?影片還是圖片序列?

向量與點陣的比較
影片與圖片

衡量「檔案格式的特性」與「網站需要到達支援程度」,將圖片用最合適的方式呈現可以大幅增進網頁效能與加載時間。如果不確定格式支援程度可以到 Can I use🔗 網站上輸入格式名稱,了解網站客群有沒有在範圍之內?能不能使用更新型的檔案格式?以下是我對常用格式的濃縮建議:

  • .jpg:老牌點陣圖片格式,中規中矩。
  • .png:老牌點陣圖片格式,如果需要透明背景或高品質無損圖片時使用。
  • .gif:老牌點陣動圖格式,大小、效能以及品質極差。
  • .webp🔗:集 .jpg.png.gif 功能於一身,優越的壓縮比。
  • .svg:老牌向量格式,可行內置入於 HTML 中。
  • .mp4:老牌影片格式,中規中矩。
  • .webm🔗:專為網頁而生的影片格式,優越的壓縮比。

適當的壓縮

SVG 可以使用 SVGOMG🔗 來壓縮,要剪輯圖片或影片可以使用 EZGIF🔗。一般來說我建議在輸出圖片時會選擇 80% 的品質,並且沒有特殊需求就盡量在 300kb 以下,自行權衡「品質」與「檔案大小」哪個比較重要。

節省外部請求

每當索取一張圖片就是多一次對外部的請求,如果圖片數量龐大將會造成負擔,因此有各種方法來「減輕」大量對外請求的技巧,像是:

  • 行內 SVG: 將 SVG 寫入 HTML 中
  • Data URI🔗: 將圖片以 Base64 編碼🔗的方式直接寫入 HTML 中
  • Image Sprite🔗:把多張圖片組為單一張圖節省外部請求
  • Icon Fonts:將多個圖像製作為字體使用

以上的做法有好有壞,由於是額外的主題,因此本篇文章不會深入討論。

<!-- 外連圖片 -->
<img src="orange-cat.svg" alt="一隻橘色的貓" />
<!-- 行內 SVG -->
<svg>
<!-- 添加 SVG Title 用於描述圖片 -->
<title>一隻橘色的貓</title>
<!-- ... -->
</svg>
<!-- Data URI -->
<img
src="data:image/png;base64,iVBORw0KGgoAAA
ANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4
//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU
5ErkJggg=="
alt="Red dot"
/>

background-image 的使用

只適合用於載入「非重要裝飾性質的背景圖片」!

使用這項屬性並沒有錯,只是它應對一個特定的使用情境:「背景圖片」,誤用濫用將帶來以下麻煩問題:

留意 SEO

background-image 作為背景裝飾性圖片,天生就不會被 Google 搜尋引擎給收錄。如果圖片希望被搜尋引擎偵測到,就應當使用 <img> 並撰寫良好的 alt 說明。

留意無障礙體驗

除了搜尋引擎的問題,螢幕閱讀器🔗也會完全忽略 background-image,如果圖片具備純粹裝飾以外的意涵也不應該設置為背景圖片。

留意效能與加載順序

通常背景圖片很少做到能夠依照裝置大小以及裝置像素密度來替換圖片,雖然可以使用 Media Queries🔗image-set🔗

// 768px 時替換合適的圖片
.hero {
background-image: url('/image.png');
}
@media only screen and (min-width: 768px) {
.hero {
background-image: url('/image-768.png');
}
}
// 1268px 時替換合適的圖片
@media only screen and (min-width: 1268px) {
.hero {
background-image: url('/image-1268.png');
}
}
.hero {
background-image: image-set(url('/image-1x.png') 1x, url('/image-2x.png') 2x);
}

但除了十分攏長之外仍有許多需求無法達成,像是:提供不同的圖片格式作為後備方案、懶加載、加載順序……等。

圖片加載順序的比較

在外連樣式的情況下,也會導致需要等待 CSS 下載完畢開始解析的時候才能開始加載圖片,相比於使用 <img> 引入圖片的方式就會讓圖片加載速度受到影響,需要使用行內樣式、預加載🔗圖片、預連接🔗等方式來避免該問題。

<img> 的使用

最常見添加圖片至網頁的方式,該元素合適用絕大多數時機,但仍需留意細節屬性的設置

撰寫替代描述 alt 屬性

如果你重視 SEO,就應該為每張圖片添加描述良好的 alt,並盡可能養成描述清楚圖片的習慣!

底下有一張很明顯的橘色貓咪圖,但如果是使用螢幕閱讀器🔗,或是搜尋引擎、網頁爬蟲想要了解網站圖片內容就會出現無法解讀的問題,為了解決該狀況,就務必要為每一個 <img> 添加 alt 屬性。

一隻橘貓在人行道上曬太陽睡覺

沒有為圖片撰寫良好的替代描述,將會導致搜尋引擎、部分網站用戶無法正確的了解圖片內容。撰寫圖片的描述可以參考「Google 搜尋中心提供的建議🔗」,更好的的描述網頁圖片。

<!-- 😟 壞 -->
<img src="cat.jpg" />
<!-- 😑 可以更好 -->
<img src="cat.jpg" alt="貓" />
<!-- 😊 好 -->
<img src="cat.jpg" alt="一隻橘貓在人行道上曬太陽睡覺" />

適當的懶加載

如今大多瀏覽器已經支援原生的懶加載🔗,只需要添加 loading=lazy 屬性即可。善用懶加載可以讓圖片在真正需要用到的時候才開始載入。

<img loading="lazy" src="cat.jpg" alt="一隻橘貓在人行道上曬太陽睡覺" />

搭配 src-setsizes 加載合適尺寸的圖片

可以額外使用 src-set 屬性在網頁中加載適當尺寸的圖片,例如:當圖片在高解析度的裝置上顯示時,就使用較大的圖片,而在低解析度的裝置上就可以使用較小的圖片。

<img
srcset="
/image.png?width=100 100w,
/image.png?width=200 200w,
/image.png?width=400 400w,
/image.png?width=800 800w"
/>

像是以上範例,當裝置寬度為 100px 時就會使用 100w 的圖片,當裝置寬度為 200px 時就會使用 200w 的圖片,以此類推。不只「裝置尺寸」,連「像素密度」也會被納入考量,例如:100w 的圖片在 2x 的像素密度下就會使用 200px

sizes 由一個媒體條件和大小值所組成,並用逗號分隔,主要功能是讓瀏覽器知道該圖片在不同裝置尺寸下會需要呈現大小,舉例來說:

<img
srcset=""
sizes="
(max-width: 400px) 200px,
(max-width: 800px) 100vw,
50vw"
/>

舉以上的例子來說瀏覽器會知道該圖片在 400px 以下時會是 200px,在 400px800px 之間時會是 100vw,在 800px 以上時會是 50vw;假設目前裝置尺寸是 900px 那麼該圖片就會是 50vw * 900px = 450px 的大小,如果像素密度是 2x 那麼就會是 450px * 2 = 900px

手動計算這些尺寸真的很累人 😅,因此可以使用 Responsive Image Breakpoints Generator🔗 來自動計算,或者是使用 RespImageLint🔗 自動化的檢測圖片的 srcsetsizes 是否正確。

提供明確的圖片比例

為了避免布局偏移🔗,在提供圖片的同時最好同時附上圖片的寬高讓瀏覽器提前知道需要預留多少空間給該張圖片:

<img width="600" height="400" />

或是使用 CSS 屬性 aspect-ratio 來標註圖片的比例關係:

<img style="aspect-ratio: 4 / 6" ; width="100%" />

非同步圖片解碼

此外還有 decoding="async" 屬性,能夠讓瀏覽器將解析圖片的工作移到主執行序之外,建議在畫面之外的圖片使用🔗

<img decoding="async" />

安排圖片的資源優先程度

此外也可以手動調整圖片獲取上的重要程度,讓瀏覽器在第一時間加載真正重要的圖片,例如 LCP 圖片🔗

<img fetchpriority="high" />

或是降低獲取重要程度,舉例來說畫面首頁輪播的後幾頁:

<div class="carousel">
<img fetchpriority="high" />
<img fetchpriority="low" />
<img fetchpriority="low" />
</div>

<picture> 的使用

提供替代圖片

舉例來說你想使用 .webp 格式來取代目前的 .png,但並不是每個用戶的瀏覽器都會支援,因此這時候就可以使用 <picture> 來達成提供替代的圖片格式:

<picture>
<source srcset="image.webp" type="image/webp" />
<img src="image.png" alt="介紹文字" />
</picture>

以上的意思是如果有支援該格式就使用該 <source> 的內容,如果沒有就退而使用 <img> 來顯示。

提供藝術呈現的圖片替換

藝術呈現指的是在不同的裝置尺寸下使用不同的圖片,例如在桌機上使用一張圖,而在手機上使用不同內容的圖,舉更為實際的例子來說:你可以使用 <picture> 在有寬敞的空間的時候顯示完整的 Logo ,但當空間不足時就使用簡化的小 Logo。

<picture>
<!-- 在大螢幕上使用大圖片 -->
<source media="(max-width: 767px)" srcset="small-logo.png" />
<!-- 在小螢幕上使用小圖片 -->
<source media="(max-width: 480px)" srcset="tiny-logo.png" />
<!-- 後備方案 -->
<img src="fallback-logo.png" alt="Logo" />
</picture>

總結

我的經驗是當網站效能出現問題時再特別做優化即可,基礎的替代文字、圖片尺寸屬性都撰寫清楚通常就足夠了。

  • 如果想要在不同裝置尺寸下基於藝術呈現顯示不同的圖片,使用 <picture>
  • 如果想要提供替代的圖片格式,使用 <picture>
  • 如果是不重要的背景圖片,可以使用 background-image
  • 使用 srcset 來提供不同裝置尺寸下的圖片,使用 sizes 來提供圖片在不同裝置尺寸下的大小
  • 使用 widthheight 或是 aspect-ratio 來提供圖片的比例關係
  • 使用 alt 來提供圖片的替代文字
  • 使用 loading 屬性來懶加載圖片
  • 使用 fetchpriority 屬性來安排圖片的資源優先程度
  • 使用 decoding 屬性來讓瀏覽器將解析圖片的工作移到主執行序之外

參考資料