圖片最佳化(Image Optimization)
30 Jul 2018實作「圖片最佳化(Image Optimization)」的第一個問題是「真的需要這個圖檔嗎?」,去除不需要的圖檔即可減少 HTTP Request。第二個問題才是「若圖檔不能去除或被取代,可以做哪些優化?」。
#1 取代圖檔
- 圖檔中若需要文字,可使用 Web Fonts 而非直接壓字在圖上,這樣文字就能被選取、搜尋和縮放,在高解析度螢幕下也能清晰。點此看如何自製 Icon Fonts 將圖畫出來(註 1)。
- 將特殊效果從圖片移除,像是文字、陰影和漸層,改用 CSS 繪製,CSS 特效與動畫不受縮放和解析度調整的影響。
- icon 可使用 Base64 Images 將圖片轉為編碼字串(註 3),讓開發者能將圖檔嵌入程式碼,以減少 HTTP Request。But,最重要的就是這個 But(註 2)!
備註
- 註 3:點此參考圖片轉 Base64 的工具。
#2 選擇適合的圖片類型並壓縮
在高解析度螢幕下,優先選用向量圖檔;若使用點陣圖檔,則高解析度的螢幕需要高解析度的圖片才能讓畫面更清晰,但檔案大小較大,必須儘量優化。
如何選擇適合的圖片類型?
如何在這兩者之間做選擇?向量圖檔(Vector images)和點陣圖檔(Raster images),兩者差別在於是否受縮放和解析度調整的影響。
- 向量圖檔:不受縮放和解析度調整的影響,適合表達幾何圖形,例如 SVG。
- 點陣圖檔:會受縮放和解析度調整的影響,可能需要準備各種不同版本的圖檔以備不同解析度的需求,適合表達不規則的複雜圖形、自然界的影像,例如 GIF、PNG、JPEG、JPEG-XR 和 WebP。
最佳化向量圖檔
最佳化 SVG 的方式是
- 使用工具去除多餘資訊,例如:SVGOMG、svgo、SVGO Compressor plugin(Sketch plugin),可大幅減少檔案體積。
- 製作 SVG 圖檔時注意 (1) 使用已定義好的
<rect>
、<circle>
、<ellipse>
、<line>
、<polygon>
而非<path>
;(2) 儘量簡化圖檔複雜度。減少程式碼數量和複雜度意味著瀏覽器能較快做好像素繪製和渲染工作,讓使用者早點看到畫面;(3) 避免使用群組(groups);(4) 避免特效;(5) 避免內嵌點陣圖檔,這對 SVG 圖檔呈現效果不佳。 - 傳輸資料時使用 GZIP 或 Brotli 壓縮,可減少約 50% 的體積。
- icon 可組合為 SVG Sprites,推薦工具-svg-sprite、IcoMoon、gulp-svg-sprite(註 4)。
備註
- 註 1:小圖示到底要使用 SVG 還是 Icon Fonts 比較好呢?當然是 SVG 完勝 d(`・∀・)b,點此看比較。
- 註 2:SVG 圖檔不要再轉成 Base64,因為檔案大小反而會變大,但可直接在 Data URI 中包含
<svg>
和進行優化,這樣檔案會更小,請參考這裡。 - 註 4:可參考我所實作的範例-SVG Sprites。
最佳化點陣圖檔
最佳化點陣圖檔的方式是(註 5)
- 針對需求選擇不同壓縮技術的圖檔格式,例如 GIF、PNG、JPEG、JPEG-XR 和 WebP。
- 刪除中繼資訊,可使用工具-ImageOptim。
- 圖片可選擇失真與否、品質參數。
- 為
<img>
設定 width 和 height,若不設定網頁可能需要重新 Layout。 - 響應式網頁會根據螢幕大小和解析度決定圖檔尺寸,因此為了顧及圖檔解碼和調整大小的成本,避免放置過度不合尺寸的圖片,可使用
srcset
和sizes
,這樣就可以根據不同的 viewport 或像素密度(device pixel ratio)來決定要載入哪一個圖檔(範例);或做適當剪裁、縮放。
#3 選擇適合的圖片格式和相關壓縮設定
選擇正確的圖片格式(註 5)。
圖片來源:Image Optimization
一些技巧…
- 優先選擇通用格式:GIF、PNG、JPEG。注意,通常 H.264 MP4 比 GIF 的檔案更小;PJPEG 的壓縮比率更好且讓使用者感知圖像加載速度更快,推薦製作 PJPEG 的工具。
- 針對選定的格式選取相關壓縮設定,例如:品質、調色盤大小等。
- PNG:pngquant + advpng
- 若支援度允許,可使用 WebP 或 JPEG XR。WebP 平均可以使檔案大小縮減 30%。
- 使用適當的圖片大小,可減少不必要的像素數量。
- 利用工具和服務將圖片最佳化的工作自動化,推薦工具-imagemin、libvips。
備註
- 註 5:可參考我所實作的範例-圖片壓縮,主要是推薦圖片壓縮和效能檢測的工具,含去除中繼資料、壓縮圖檔、產生不同尺寸或格式的圖片、Gulp 設定和使用 Lighthouse 做網站檢測。
#4 Responsive Images
響應式網頁會根據視窗大小決定圖檔尺寸,因此為了顧及圖檔解碼和調整大小的成本,避免放置過度不合尺寸的圖片,可使用 <picture>/<img srcset sizes>
。
說明和範例可參考這裡。
#5 Image Caching
正確設定快取規則可減少圖片下載數,請參考-HTTP Caching。
#6 Resource Hints
預先下載資源可讓使用者有更快看到畫面的錯覺。
主要會用到 preload
,點此比較 Preload 與 Prefetch 的異同。
preload
:提示瀏覽器儘早下載當前頁面需要的資源,可為資源設定優先權。
注意,不需預先下載 (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 新版瀏覽器是支援的。
其他還有
dns-prefetch
:預先做 DNS 解析(domain name resolution)。prefetch
:提示瀏覽器預先下載之後需要的資源。preconnect
:提示瀏覽器針對特定資源先做 DNS 查詢、TCP handshake 以與 TLS negotiation,多用於請求第三方資源。prerender
:提示瀏覽器預先下載並執行之後需要的資源。
#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 測試,報告如下圖
其中,關於「效能」首要的改善建議是
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 Loading,避免預先載入使用者可能看不到的圖片,直到使用者瀏覽到該圖片的時候才去抓圖。
加入後,關於圖片延遲載入的建議便消失了(其實也不是消失,只是好很多 XD),繼續改進其他項目 (๑•̀ㅂ•́)و✧
Demo 成果。
#9 Sprites
由於取得每張圖片都需要一次 HTTP 請求,因此將小圖併為一張圖,這樣就只需要一次 HTTP 請求,優點是減少 HTTP 請求次數即可減少畫面載入時間,缺點是 (1) 需要使用額外工具維護;(2) 圖檔編排可能需要空白而導致檔案體積較大;(3) 樣式定位若沒有設定好會有滲透(Bleedthrough)的問題。
解決方法:(1) 使用 HTTP/2,由於 HTTP/2 沒有對於每個伺服器的發送請求數的限制、能多工連線以交錯取得 frame 來避免特定資源過度佔用連線、自動決定檔案下載的優先權等,因此不將圖檔併為 Sprites 而是拆成多個圖檔,對於效能是更好的;(2) 使用 Data URI 並做 Gzip 壓縮,這樣連取得 Sprites 的請求都不需要了,也改善維護、因空白導致檔案體積變大、定位錯誤而滲透的問題。
#10 利用工具檢視效能
簡單地手動檢測
- Lighthouse:Lighthouse 可檢測加載效能、無障礙、PWA(Progressive Web App)和 SEO,提出潛在問題與解法,解法都附有官方詳細說明。
- Cloudinary Image Analysis:Cloudinary Image Analysis 可檢測該網頁圖片的狀況和潛在可優化的項目,例如對於個別圖片在不同檔案格式下,還能壓縮多少體積,若有針對不同瀏覽器支援更先進的檔案格式(例如:針對 Chrome 提供 Webp 檔案)就可參考一下,會省很多空間的!用它們家的 CDN 做圖檔優化也是不錯的選擇喔。
更多關於加載效能檢測工具的試用可參考這裡。