為什麼圖片優化值得獨立一章#
在大部分網頁中,圖片是所有資源裡佔比最大的一類。最直覺的優化方式就是把圖片變小,但實務上要考量的因素遠不只檔案大小:使用者的網路速度、裝置的圖片解碼效能、螢幕特性等都會影響最終體驗。本章從 debug 視角整理「品質與速度權衡」下常見的圖片優化手段。
從建構視角的整理可參考
02-performance-optimization/02-resource-optimization/02-image-optimization,兩篇互補閱讀。
先思考:真的需要點陣圖嗎#
在採用 JPG、PNG 這類點陣圖之前,建議先停下來檢視幾個問題:
- 是不是可以用文字直接表達,而非把文字嵌在圖裡?
- CSS 是否可以模擬同樣的視覺效果?
- 內容是否可以改用 SVG?
點陣圖檔案通常較大,下載與解碼也都會耗用較多瀏覽器資源,更容易拖慢使用者體驗,所以能不用就盡量不用。
SVG:第一順位的考量#
如果一定要使用圖片,先評估 SVG:
- 屬於向量圖,無論放大多少倍都不會模糊。
- 相較於點陣圖,檔案通常小很多。
- 傳輸時可以再透過 Gzip、Brotli 等壓縮格式降低體積。
哪些題材適合 SVG#
- 商標
- 圖示
- 圖表
- 地圖
哪些點陣圖適合改寫成 SVG#
- 尺寸偏小的圖
- 由幾何形狀組成的圖
- 不希望出現失真或模糊的圖
讓 SVG 更小#
SVG 本身也可以透過工具進一步壓縮,例如 SVGO ↗ 就能移除多餘的標記與屬性。
點陣圖的壓縮策略#
點陣圖最直接的優化方式就是壓縮,主要可分為兩種:
- 有損壓縮(Lossy Compression):例如 JPG,藉由近似或抽樣的方式減少資料量,壓縮後無法還原。
- 無損壓縮(Lossless Compression):例如 PNG,以資料重建的方式縮小檔案,不會折損畫質。
選哪一種要看網站特性,例如使用者是否能接受些微失真、是否更在意檔案大小。實務上有幾個通用原則:
- 圖片可能附帶拍攝地點、相機資訊等中繼資料(EXIF),可透過工具清除以減小體積。
- 盡量讓圖片實際尺寸與顯示尺寸接近,避免瀏覽器在運行時做大量縮放。
- 把圖片優化整合進構建流程(例如以 Webpack 串接自動化工具),就不會仰賴人工記得壓縮。
- 有損壓縮工具通常可以調整品質參數,視覺差異往往沒有想像中明顯,最佳值仍需要反覆測試確認。
WebP#
除了 IE 之外,主流瀏覽器都支援 WebP,作為網頁圖片基本上是首選:
- 在同樣畫質下比 PNG、JPG 小得多。
- 支援動圖(GIF 可改為 WebP,但 WebM 通常會更小)。
- 支援透明(JPG 不支援)。
進階格式還有 AVIF、Progressive JPEG 可考慮;WebP 是目前相容性與壓縮率較佳的折衷選擇。
常用工具#
- Imagemin ↗:插件化的圖片壓縮工具,可搭配 imagemin-webpack-plugin ↗ 整進建構流程。
url-loader:以 Webpack 打包圖片時,當圖片小於limit設定值,會被轉成 DataURL(Base64)內嵌在引入檔案中。雖然會讓主檔變大一點,但可以省下一次額外的 HTTP Round-trip。
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: "url-loader",
options: {
limit: 8192,
},
},
],
},
],
},
};動圖:別再用 GIF#
GIF 體積偏大,不建議直接放到網頁上。可以用 FFmpeg 等工具把 GIF 轉成 MP4 或 WebM;若不需要支援 IE,WebM 通常是最小巧的選擇。
HTML 的 <video> 元素配合幾個屬性,就能模擬出 GIF 自動播放、循環、無聲、不全螢幕的行為,唯一差別是不能直接以 img 載入。
<video autoplay loop muted playsinline>
<source src="my-animation.webm" type="video/webm" />
<source src="my-animation.mp4" type="video/mp4" />
</video>透過
source與type的組合,不支援 WebM 的瀏覽器會自動退回到 MP4。
響應式圖片(Responsive Image)#
在 img 元素上補上幾個屬性,瀏覽器就會根據使用者的螢幕大小,挑選最合適的圖片載入,兼顧畫面品質與流量效率:
<img
src="flower-large.jpg"
srcset="flower-small.jpg 480w, flower-large.jpg 1080w"
sizes="(max-width: 600px) 480px, 50vw"
/>srcset#
列出可用的圖片檔名與其實際像素寬度,由瀏覽器決定最終載入哪一張。
sizes#
讓你以 Media Query 的形式描述「在不同情境下,這張圖在版面上的 CSS 寬度為何」(CSS 仍要另外設定)。瀏覽器會結合 sizes 與裝置 DPR、DPI,挑選 srcset 中最適合的圖片。
sizes 可以使用各種長度單位、calc,但不接受百分比。
src#
當瀏覽器不支援 srcset 與 sizes 時,會 Fallback 到 src,因此放在 src 的圖片應能涵蓋多數裝置可能用到的尺寸。
DPR 與 DPI#
- DPR 是「一個 CSS Pixel 對應多少個實體 Pixel」的比例,例如 iPhone X 的 DPR 為 3,等於每個 CSS Pixel 實際用 3 × 3 = 9 個實體 Pixel 顯示。
- DPI 是「每英吋有多少個 Pixel」,DPI 高的裝置上每個 Pixel 都很細密,因此若以原生 Pixel 為單位設計版面,元素會小到難以辨識。
圖片調整工具#
- ImageMagick ↗:經典的命令列圖片處理工具。
- sharp ↗:速度更快,且以 npm 套件形式發行,便於整合進 Node.js 流程。
圖片 CDN#
提供即時轉檔、優化與快取,搭配 CDN 讓圖片傳輸更快,常用網址參數即可指定尺寸轉換:
https://<thumbor-server>/300x200/原始圖片網址.png
- thumbor ↗:可自架的開源伺服器。
- Cloudinary ↗:功能與文件較齊全,但用量大需付費。
Art Direction:不同螢幕餵不同圖#
某些圖片直接等比例縮到手機寬度會變得難看,這時可以利用 <picture> 與 source 為不同螢幕提供不同構圖。<picture> 會回傳第一個 Media Query 為真的 source,否則退回 img。當 source 已用 Media Query 區分,就不需要再寫 sizes。
<picture>
<source media="(max-width: 799px)" srcset="elva-480w-close-portrait.jpg" />
<source media="(min-width: 800px)" srcset="elva-800w.jpg" />
<img src="elva-800w.jpg" alt="Chris standing up holding his daughter Elva" />
</picture>常見問題#
為什麼不用 JavaScript 來實作響應式圖片#
因為 HTML Parser 會在 JavaScript、CSS 解析完成前就開始下載圖片,這也是為什麼響應式圖片需要靠 srcset、sizes 這類 HTML 屬性提供尺寸資訊,讓瀏覽器在最早的時間點就能判斷該抓哪一張。
那一張響應式圖要準備幾個版本#
實務上多以 3 至 5 張為基準。版本越多,使用者體驗與效能調整空間越大,但也會增加伺服器儲存與 HTML 撰寫成本。
怎麼決定 sizes#
提供多段 sizes 區間能涵蓋更多情境;若資源有限,可參考 GA 等分析工具搭配 screensiz.es
↗ 找出主要使用者的螢幕寬度,再以此設計斷點。若僅打算用一張圖,請確保它夠大、能涵蓋多數裝置螢幕。
原文出處#
- 原書/iThome:https://ithelp.ithome.com.tw/articles/10252501