Eden Pan 已發佈 2019-11-22

如何最佳化網站的圖片效能?

圖片是網站很重要的視覺元素,無論是 Banner、Icon、Logo、各種圖表等等,佔據了網站絕大多數的可視空間,因此如何優化圖片就成了前端工程師一門重要的學問。本文主要是針對引入正確的圖片類型,並正確呈現圖片尺寸與像素在不同裝置上來談談圖片的最佳化過程。

Step1、選擇正確的的圖片類型

圖片主要分為向量(Vector)和點陣圖片(Raster)兩種:

  • 點陣(Raster)圖片:GIF、PNG、JPEG 或 JPEG-XR 和 WebP 等。
  • 向量(Vector)圖片:以 SVG 為主。

實務上最常遇到就是 PNG、JPG、SVG 三種,它們有何不同呢?這裡放了三張不同圖片類型的狗狗圖片。

<img src="./images/dog.jpg" alt="狗狗 jpg" />
<hr />
<img src="./images/dog.png" alt="狗狗 png" />
<hr />
<img src="./images/dog.svg" alt="狗狗 svg" />

images

這三張狗狗圖片,第一張是 JPG、第二張是 PNG、第三張是 SVG,看起來差別不大,但佔的空間卻不太一樣。

images

可以看到 Size 欄位,同一張圖片從最輕量到最肥厚分別是:SVG 的 2.5K > JPG 的 5.7K > PNG 的 9.9K,最大跟最小差了快四倍。

再來比較一下解析度,我們把三張圖片放大兩倍來看,首先是 JPG:

images
恩,超糊的,毛邊變得非常明顯。再來看一下 PNG:

images
稍微好一些,但還是有點糊。最後是 SVG:

images
基本上根本沒差,跟小圖一樣清晰。所以就解析度來說從清楚到模糊分別是:SVG > PNG > JPG。

會這樣是因為向量圖片跟點陣圖片構成的因素所致。向量圖片(SVG)是由簡單幾何圖形組成,輕量且畫素清晰,缺點是無法處理複雜的畫面,例如 Banner 等。而點陣圖片(PNG、JPG)則會將完整像素呈現在網格內。兩者各有優缺點。

所以結論就是:

  • 內容式圖片優先使用 => PNG 或 JPG(看對解析度或網站 Loading 速度的要求)。
  • Icon 或 Logo 優先使用 => SVG 或 PNG。

Step2、呈現正確圖片尺寸與像素在不同裝置上

有開發者會直接放一張大圖去適應所有的裝置或螢幕寬度,原因可能是因為追求圖片在任何裝置都有高解析度,但這其實對網站的 Performance 不太好,因為圖片的尺寸越大、像素越高 Loading 的成本也越高,這個現象在 Mobile Devcie 會更明顯。

請盡量在正確的裝置使用正確尺寸的圖片,才不會浪費圖片的資源。

要達到這樣的效果可以善用 img tag 的 srcset 與 sizes 這兩種屬性,才能兼顧不同裝置的圖片呈現。

假設我們要在頁面做出這個樣式:

images

需要放一張大圖在頁面上,那 img tag 我們通常會這樣寫:

<img src="./images/large.png" alt="這是一張圖片" />

這樣寫不是不行,只是必須要考慮到圖片的大小對不同裝置的影響,可以用 Chrome Dev 看一下 Network 的 Img 資訊:

images

可以看到圖片佔了 1.7M 的空間,一樣的 Size 如果也直接放到 Mobie Device 上那就不太好了。這個時候可以考慮這樣做:

<img 
   src="./images/large.png" 
   srcset="./images/large.png 1x, ./images/small.png 2x" 
   alt="這是一張圖片" 
/>

首先這裡會看到一個新的屬性 srcset,可以針對頁面不同的裝置像素比例(DPR) 指定該放哪張圖片。關於 DPR 一般來說 PC 通常都是 1x,Mobile 則是 2x,可以在 Chrome DevTool 的 console 下 window.devicePixelRatio 查詢。

這裡 srcset 的內容是指:若 DPR 為 1x 的時候使用 large.png,若DPR 為 2x 的時候使用 small.png。瀏覽器不支援 srcset 屬性才會去抓 src。

再回頭檢視剛才的頁面,就會發現 Mobie Device 的圖片已經改為 small.png,所佔據的空間也已降為 428K。

images
images

空間釋出將近 70%,是一個很好的優化圖片方式。

如果不知道 DPR 是多少的話也有另一種寫法:

<img
  src="./images/large.png"
  srcset="./images/large.png 1000w, ./images/small.png 500w"
  sizes="(max-width: 500px) 60vw, 100vw"
  alt="這是一張圖片"
/>

這裡 srcset 裡面的單位 w 是指螢幕寬度,只要給 w 值瀏覽器就會去計算 w 乘以 DPR 的值,並取出對應或最接近尺寸的圖片。

而 sizes 的作用是什麼呢?主要是瀏覽器在載入頁面時並不知道圖片大小,預設寬度會設在 100vw,若需要在不同螢幕尺寸動態調整圖片寬度的話就必須加上 sizes 屬性,讓瀏覽器判斷並取出對應圖片。

例如上面這一段 Code 是指螢幕寬度 500px 以下時圖片寬度要改成 60%,不然就維持 100%,瀏覽器就會去計算圖片的尺寸: 500(螢幕寬度) 乘以 2 (DPR)乘以 0.6(60vw) = 600px,所以就會選擇 small.png 這張圖片。

注意!!!這裡加上 sizes 屬性只是告知瀏覽器計算圖片的基準,如果要實際調整圖片樣式還是要透過 CSS 喔。

另外還有一種彈性比較好的寫法:

<picture>
  <source
    media="(max-width: 500px)"
    srcset="./images/large.png 1x, ./images/small.png 2x"
  />
  <img 
    src="./images/large.png" 
    alt="這是一張圖片" 
  />
</picture>

為什麼說比較有彈性呢?除了可以把 sizes 跟 srcset 拆成另一個 source tag 易讀性較高之外,還可以支援不同的圖片類型,例如:

<picture>
  <source srcset="test.webp" type="image/webp">
  <img src="./images/large.png" alt="這是一張圖片">
</picture>

如果有支援 webp 類型就用 source tag 內的屬性,不行的話就用 img tag。但目前 picture tag 尚不支援 IE,所以有跨瀏覽器需求的話還是用 src 比較安全。

images

結論

  • 可以根據專案不同的需求放置不同的圖片類型,例如如果網站追求要快,那 Banner 就用 JPG,如果像素不能太差,那就用 PNG。而 Logo 以及 Icon 請盡量使用 SVG。
  • 多多善用 img tag 的 srcset 屬性,會帶給網站更大的效益。
  • 其實這裡還有圖片壓縮沒有寫到,但我想這應該是常識所以就不多說了。
  • 其實以上方法比較適用在圖片來源控制在前端自己手上的時候,如果圖片是來自 API 或後端的話,可能就要先跟後端溝通好規格囉。

參考資料

  1. Responsive Images
  2. 響應式圖片(Responsive Images)
  3. Responsive images

關於筆者

暱稱:Eden Pan

介紹:前端工程師 - 遠距工作者 - 奶爸 我的 Medium => https://medium.com/@edenpan

文章列表 文章列表