圖片最佳化(Image Optimization)

實作「圖片最佳化(Image Optimization)」的第一個問題是「真的需要這個圖檔嗎?」,去除不需要的圖檔即可減少 HTTP Request。第二個問題才是「若圖檔不能去除或被取代,可以做哪些優化?」。

#1 取代圖檔

備註

#2 選擇適合的圖片類型並壓縮

在高解析度螢幕下,優先選用向量圖檔;若使用點陣圖檔,則高解析度的螢幕需要高解析度的圖片才能讓畫面更清晰,但檔案大小較大,必須儘量優化。

如何選擇適合的圖片類型?

如何在這兩者之間做選擇?向量圖檔(Vector images)和點陣圖檔(Raster images),兩者差別在於是否受縮放和解析度調整的影響。

最佳化向量圖檔

最佳化 SVG 的方式是

備註

最佳化點陣圖檔

最佳化點陣圖檔的方式是(註 5

#3 選擇適合的圖片格式和相關壓縮設定

選擇正確的圖片格式(註 5)。

圖片壓縮格式

圖片來源:Image Optimization

一些技巧…

備註

#4 Responsive Images

響應式網頁會根據視窗大小決定圖檔尺寸,因此為了顧及圖檔解碼和調整大小的成本,避免放置過度不合尺寸的圖片,可使用 <picture>/<img srcset sizes>

說明和範例可參考這裡

#5 Image Caching

正確設定快取規則可減少圖片下載數,請參考-HTTP Caching

#6 Resource Hints

預先下載資源可讓使用者有更快看到畫面的錯覺。

主要會用到 preload點此比較 Preload 與 Prefetch 的異同。

注意,不需預先下載 (1) 沒有受到廣泛瀏覽器支援的格式的圖檔,例如 WebP;(2) 響應式網頁下使用 srcset 根據設備條件定義的圖片。

提示瀏覽器當前頁面會用到 script.js,儘早下載它,屬性 as 用來告知資源的類型,若不設定則優先權會非常低。

<link rel="preload" href="script.js" as="script" />

也可使用 HTTP Link header。

Link: <https://example.com/script.js>; rel=preload; as=script

屬性 onload 可在下載完成後做些事情。

<link rel="preload" as="script" href="script.js" onload="console.log('Hello World!')">

屬性 media 可依需求下載不同資源。

<link rel="preload" as="image" href="map.png" media="(max-width: 600px)">
<link rel="preload" as="script" href="map.js" media="(min-width: 601px)">

在瀏覽器支援度上,桌機方面,Chrome 和 Safari 有支援、Edge 部份支援、Firefox 和 IE 不支援;手機方面,iOS Safari、Chrome for Android 新版瀏覽器是支援的。

preload 瀏覽器支援度

圖片來源:Resource Hints: preload

其他還有

#7 CDN

CDN 透過設備的檢測、即時圖檔優化(調整大小、剪裁和過濾)和目前離使用者距離最近的 CDN 快取來提供圖片,比較服務

我是使用 Cloudinary,關於這個服務的相關資料,可參考

#8 Lazy Loading

避免使用者下載用不到的資源,推薦工具

簡單範例如下,先使用 placeholder 佔用空間,等使用者滑到該位置前,再用 JavaScript 將正確的圖片替換掉 placeholder。

<img>

<img
  class="lazy"
  src="placeholder-image.jpg"
  data-src="image-to-lazy-load-1x.jpg"
  data-srcset="image-to-lazy-load-2x.jpg 2x, image-to-lazy-load-1x.jpg 1x"
  alt="I'm an image!"
>

範例

CSS Background

.lazy-background {
  background-image: url("hero-placeholder.jpg"); /* Placeholder image */
}

.lazy-background.visible {
  background-image: url("hero.jpg"); /* The final image */
}

範例

範例

吃什麼,どっち」專案中使用 react-lazy-load 來做到圖片延遲載入的效果。

加入 Lazy Load 前,經由 Lighthouse 測試,報告如下圖

Lighthouse 網站檢測

Lighthouse 網站檢測

Lighthouse 網站檢測

其中,關於「效能」首要的改善建議是

Defer offscreen images

Consider lazy-loading offscreen and hidden images after all critical resources have finished loading to lower time to interactive. Learn more.

加入 Lazy Load 前

解決方式是採用 Lazy Loading,避免預先載入使用者可能看不到的圖片,直到使用者瀏覽到該圖片的時候才去抓圖。

加入後,關於圖片延遲載入的建議便消失了(其實也不是消失,只是好很多 XD),繼續改進其他項目 (๑•̀ㅂ•́)و✧

加入 Lazy Load 後

Demo 成果。

#9 Sprites

由於取得每張圖片都需要一次 HTTP 請求,因此將小圖併為一張圖,這樣就只需要一次 HTTP 請求,優點是減少 HTTP 請求次數即可減少畫面載入時間,缺點是 (1) 需要使用額外工具維護;(2) 圖檔編排可能需要空白而導致檔案體積較大;(3) 樣式定位若沒有設定好會有滲透(Bleedthrough)的問題。

解決方法:(1) 使用 HTTP/2,由於 HTTP/2 沒有對於每個伺服器的發送請求數的限制、能多工連線以交錯取得 frame 來避免特定資源過度佔用連線、自動決定檔案下載的優先權等,因此不將圖檔併為 Sprites 而是拆成多個圖檔,對於效能是更好的;(2) 使用 Data URI 並做 Gzip 壓縮,這樣連取得 Sprites 的請求都不需要了,也改善維護、因空白導致檔案體積變大、定位錯誤而滲透的問題。

#10 利用工具檢視效能

簡單地手動檢測

更多關於加載效能檢測工具的試用可參考這裡

設定目標、自動檢測

參考資料


圖片最佳化 效能調校 加載效能 SVG Resource Hints Lighthouse Base64 Images Image Optimization Loading Performance Gzip 編碼 解碼 encode decode base-64 前端效能 系列文