為什麼圖片是優化重點#
幾乎沒有現代網頁是完全沒有圖片的,連最小的 icon 都算。根據 HTTP Archive ↗ 對前一百萬熱門網站的長期統計,圖片往往是平均佔比最大的資源類型。換句話說:只要圖片優化做得好,整體載入時間就會明顯下降。
先看格式:選對才是優化的起點#
不同格式各有適用場景,搞混的話即使再壓縮,效果也很有限。
先問自己:真的需要圖片嗎?#
如果視覺需求可以用 CSS 完成(背景色、漸層、簡單形狀),就不要使用圖片。少一張圖就少一個請求,這是最便宜的優化。
JPEG 與 PNG(點陣圖)#
兩者都是由像素陣列構成的點陣圖,是網頁最常見的格式。
- JPEG 採用有損壓縮(Lossy Compression),檔案小,但每次儲存都會再損失一點細節。適合大尺寸的照片、背景圖。
- PNG 採用無損壓縮(Lossless Compression),保留所有像素資料,邊緣清晰,適合含有文字、線條、Icon 的圖。
同樣尺寸並都壓縮過後,PNG 通常比 JPEG 大。若是會反覆編輯儲存的素材,建議用 PNG,避免 JPEG 重複壓縮造成畫質持續下降。
SVG(向量圖)#
SVG 以幾何描述構成,放大不會模糊,且通常比點陣圖小。
- 適合 icon、圖表、Logo 等以幾何元素為主的圖。
- 可以再透過 Gzip 進一步壓縮(PNG、JPEG 本身已壓縮,Gzip 效果不明顯甚至會變大)。
- 可以使用 SVGOMG ↗ 之類的工具進一步最小化。
WebP#
WebP 是 Google 在 2010 年推出的格式,支援有損與無損壓縮,並可表達動圖。檔案大小通常顯著小於 PNG 與 JPEG。
舉個直觀的例子:YouTube 影片縮圖在相同解析度下,WebP 約 195 KB,JPEG 約 285 KB。除了 IE 不支援、Safari 部分支援之外,主流瀏覽器都已經支援,是現代網頁的首選格式。
還有更新的 AVIF 格式,壓縮率比 WebP 更佳,已逐步擴大支援範圍。實作上通常會搭配 fallback 來處理。
圖片壓縮:注意取捨#
壓縮工具能顯著減少檔案大小,但要先想清楚要不要犧牲畫質:
- 有損壓縮(Lossy Compression):壓縮率高、不可逆,適合可接受些許失真的場景,如風景照。
- 無損壓縮(Lossless Compression):保留原始品質,但壓縮率較低,適合需要清晰邊緣的素材。
線上工具如 TinyPNG、jpeg-optimizer 可以快速試壓;企業級流程則會建立內部服務,或在 Webpack 等 bundler 中以 plugin 形式整合到 build 流程裡。
漸進式 JPEG(Progressive JPEG)#
相對於傳統 JPEG「由上往下」逐行載入的方式,漸進式 JPEG 一開始就先呈現一張模糊的整圖,再隨著資料補齊逐漸變清晰。對使用者而言,視覺上更早看到完整輪廓,感受到的「等待時間」會比較短。
響應式圖片(Responsive Image)#
裝置螢幕大小差異極大,硬塞同一張圖會造成兩個問題:
- 在小螢幕載入過大圖片,浪費頻寬與渲染效能。
- 在大螢幕拉伸小圖,導致模糊。
設計師交付 1x、2x、3x 三組圖,是為了配合不同 DPR(Device Pixel Ratio)。DPR 是螢幕像素與 CSS pixel 的轉換倍率,例如某些 iPhone 為 2 或 3,這時若呈現 1x 圖會變模糊。
用 srcset 處理 DPR#
<img
src="ironman.jpg"
srcset="ironman_1x.jpg 1x, ironman_2x.jpg 2x"
alt="ironman"
/>在 DPR 為 2 的裝置上瀏覽器會自動選擇 ironman_2x.jpg,找不到時 fallback 到 src。
用 srcset 加 w 描述子#
當圖只有 small / mid / large 三種版本時,可以用寬度描述子讓瀏覽器自行決定:
<img
src="ironman.jpg"
srcset="ironman_small.jpg 300w, ironman_mid.jpg 600w, ironman_large.jpg 1200w"
sizes="(max-width: 400px) 80vw, 50vw"
alt="ironman"
/>sizes 並不會改變圖片實際大小,而是告訴瀏覽器「在不同 viewport 下,這張圖會以多寬呈現」,瀏覽器再結合 DPR 與 viewport 計算出最適合的版本。
用 picture 處理 art direction#
希望在不同裝置呈現「不同構圖」時,用 <picture> 最方便:
<picture>
<source
media="(min-width: 800px)"
srcset="ironman-large-1x.jpg 1x, ironman-large-2x.jpg 2x"
/>
<source
media="(min-width: 500px)"
srcset="ironman-medium-1x.jpg 1x, ironman-medium-2x.jpg 2x"
/>
<img src="ironman-small.jpg" alt="ironman" />
</picture><picture> 內的 <source> 由上往下匹配,符合條件的第一個就被採用。也能用來解決格式相容問題:把 WebP 放第一個,瀏覽器不支援時自動往下找 PNG/JPEG。
srcset、<picture>把選擇權交給瀏覽器,下載前就決定哪一張,因此只會發出一個請求。若用 CSS Media Query 切換background-image,則可能事先下載多張,反而更耗效能。
Image CDN#
不是所有專案都能要求設計師交出多種尺寸。Image CDN(如 imgix、Cloudinary)能在請求時即時轉換格式、resize 與壓縮,並把結果放進邊緣快取。常見的用法是透過 URL 參數指定需求:
https://res.cloudinary.com/demo/image/fetch/w_200,h_200/{image-path}.jpg在 bundler 中整合#
Webpack 可透過 url-loader 把小圖直接以 base64 (DataURI) 的形式內嵌到 JS bundle,省掉一次網路請求:
module.exports = {
module: {
rules: [
{
test: /\.(png|gif|jpg)$/i,
use: [
{
loader: "url-loader",
options: {
limit: 8787, // 超過 8787 byte 仍以檔案形式輸出
},
},
],
},
],
},
};base64 的優缺點要一起考慮:
- 優點:減少 HTTP 請求,適合很少改動的小 icon。
- 缺點:編碼後體積比原檔大、不會被瀏覽器快取、修改後整段字串會變動,維護不易。
小結#
圖片最佳化的核心是「合適的格式 + 合適的尺寸 + 合適的壓縮」三者。先確認真的需要圖片,再選格式(首選 WebP / AVIF,其次 SVG / JPEG / PNG),最後用響應式圖片把不同裝置的需求對齊。每一步都不困難,組合起來就能讓網站「圖片」這個最大宗的資源大幅瘦身。