iT邦幫忙

2025 iThome 鐵人賽

DAY 14
0
Modern Web

手刻部落格,從設計到部署的實戰攻略系列 第 14

Astro(四):淺談圖片,Baseline 及 Progressive JPEG 和 WebP、AVIF

  • 分享至 

  • xImage
  •  

不知道大家有沒有瀏覽過一些網站,放了一整面 Gallery 的圖片,但是每張圖片的載入速度都相當慢,會看到從圖片的最上方往下一條一條緩慢顯示,整體顯示出來的時間甚至能達到數十秒以上。

由於部落格的文章中,圖片是重要的組成部分之一,如果載入慢、效果不佳的話便會造成不好的使用者體驗,因此,這一講我們就來淺談圖片這個話題。

來聊聊為什麼會有這個情況產生呢?怎麼讓使用者體驗更好?Astro 又有什麼內建或插件能輔助圖片的載入?

Baseline JPEG 到 Progressive JPEG

首先我們來看看「畫面一條一條往下長」的情形,其實這就是 Baseline JPEG 的典型視覺效果。Baseline JPEG 是 JPEG 規格中的一種最基本做法(Baseline Sequential DCT,8-bit、Huffman;檔案裡會用 SOF0 做標記)。

這種做法會把圖篇切成很多個 8x8 的小方塊,照著空間順序(通常為左至右、上到下)把資料寫進檔案。因此在資料還沒全到齊之前,瀏覽器就能夠邊收邊解碼,由上往下把畫面慢慢渲染出來,看起來就會是一條一條的往下長出來。

Progressive JPEG from web.dev

*Progressive JPEG from web.dev

上圖中的左右兩張 JPEG,分別展示 Baseline 及 Progressive 的載入體驗:在下載量相近的瞬間,Baseline 只顯示了部分圖片,但 Progressive JPEG 已經能看到整張(只是稍微模糊)。

為什麼會有這樣的差別?關鍵在 JPEG 檔案內部的資料擺放和解碼的順序:

  • Baseline JPEG 會依照空間次序把每個小方塊的內容寫入,所以尚未完整時就會先出現上半部,再慢慢向下顯示
  • Progressive JPEG 則是另一種 JPEG 的規格,他把同一張圖依據「明暗」及「輪廓」的資訊分成好幾輪,先記錄全圖的大致明暗度和輪廓,細節再後續補齊。因此造就一開始就看得到整張圖,然後從模糊逐漸變清楚。

產出 Progressive JPEG

如果我們的圖片使用 JPEG 格式,就要留意 Baseline JPEG 的渲染體驗可能不佳,其中一種改善方式就是不用他,改用 Progressive JPEG。

但是這種轉換涉及到編碼方式,要將 Baseline JPEG 改成 Progressive,就需要重新編碼一次。在 Node.js 中可以透過一套叫做 Sharp 的套件來達成,他是基於影像處理 Library - libvips 的 Node-API。

我們可以寫個簡單的腳本來做這件事:

import { promises as fs } from 'node:fs'
import fg from 'fast-glob'
import sharp from 'sharp'

const files = await fg('src/assets/**/*.{jpg,jpeg}', { dot: false })
await Promise.all(files.map(async f => fs.writeFile(f, await sharp(f).jpeg({ progressive: true }).toBuffer())))

其關鍵點在於 { progressive: true } 這段 Snippet,就把所有 JPEG 的編碼方式改成 Progressive。

或是直接在 Astro 的 Sharp 層(透過修改 astro.config.ts),讓輸出的 JPEG 一律為 Progressive。

import { defineConfig } from 'astro/config'
import { sharpImageService } from 'astro/assets/services/sharp'

export default defineConfig({
  image: {
    service: sharpImageService({ formatOptions: { jpeg: { progressive: true } } }),
  }
})

如果部落格需要支援老舊的瀏覽器,有 JPEG 的圖片就能使用這種 Progressive 的格式來提升體驗。但更好的方式其實是提供更現代化的圖片格式,例如 WebP 和 AVIF。

現代化的圖片格式 WebP 及 AVIF

WebP 是 Google 推出的影像格式,而 AVIF 是基於 AV1 影片編碼的影像格式,兩者在壓縮上都比傳統 JPEG 更有效,並且皆支援有損、無損的壓縮,也支援透明度。

由於這些優點,選擇更現代化的圖片格式可以讓整體網站效能夠好,所以我們在提供圖片的時候可以選擇 AVIF 作為首選、WebP 當 Fallback,最後再備用 JPEG 或 PNG 來相容較老舊的瀏覽器。

在 AVIF 和 WebP 的比較當中,近年的 Benchmark 指出 AVIF 的檔案大小通常比 WebP 更小(相同主觀的畫質下),而 WebP 的解碼速度則較 AVIF 略快。權衡之下,由於現在電腦的計算能力都很強,傳輸量少的優勢會是我們選擇 AVIF 的主因。

在 Astro 當中,我們可以在 astro.config.ts 先設定 AVIF、WebP 和 JPEG。

import { defineConfig } from 'astro/config'
import { sharpImageService } from 'astro/assets/services/sharp'

export default defineConfig({
  image: {
    service: sharpImageService({
      formatOptions: {
        jpeg: { progressive: true, quality: 80 },
        avif: { quality: 80 },
        webp: { quality: 80 },
      },
    }),
    layout: 'constrained',
    responsiveStyles: true,
  },
})

需要加入的設定其實只有 avifwebp 的兩個 formatOptions,後面的 layoutresponsiveStyles 則是自動產生響應式的影像(在 Astro 5.10 後支援,影響到 Markdown 中處理的影像)。

參考資料

  1. web.dev - Image format: JPEG
  2. Youtube - How are Images Compressed? [46MB ↘↘ 4.07MB] JPEG In Depth
  3. Youtube - Progressive decoding: JPEG vs JPEG XL vs AVIF
  4. Wiki - 位元流
  5. npm - sharp
  6. Github - libvips
  7. Wiki - AVIF
  8. Wiki - WebP
  9. web.dev - Deploying AVIF for more responsive websites

上一篇
Astro(三):用 MDX 寫文章,顯示程式碼、數學式,搭配 JavaScript 服用
下一篇
Astro(五):如何製作文章列表?簡介 Content Collections
系列文
手刻部落格,從設計到部署的實戰攻略19
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言