優化首圖體驗的終極指南:SVG + BlurHash + LQIP 實戰解密

作者: Calpa Liu
字數:4012
出版:2025 年 4 月 22 日
分類: 技術文章 前端開發 網站效能SEO
你還在用傳統圖片載入方式嗎?透過 SVG 預佔位、BlurHash 模糊預覽與 LQIP 技術,打造兼顧效能與美感的圖片顯示體驗,讓網站更快更穩更吸睛。

圖片載入對網站性能和用戶體驗有重大影響。當首圖加載緩慢時,會直接影響 Google 的核心網頁指標,如 LCP (Largest Contentful Paint) 和 CLS (Content Layout Shift)。LCP 過高意味著頁面主要內容載入時間延長,而 CLS 錯亂則可能導致頁面元素位移,影響用戶操作和閱讀體驗。

除了技術指標外,慢速載入還會造成用戶體驗下降。白屏等待期間缺乏視覺提示,容易讓用戶產生網站加載緩慢的印象。而圖片載入後引起的頁面跳動,更是破壞了穩定的閱讀體驗,可能導致用戶流失。

這個網站採用了漸進式圖片加載技術。如果您想親身體驗這項技術帶來的性能提升,可以嘗試重新加載頁面或在較慢的網絡環境下重新載入。這樣您就能清楚地看到圖片是如何從模糊到清晰逐步加載的過程,以及這種加載方式如何提高了整體的頁面加載速度和用戶體驗。通過比較不同網絡條件下的加載效果,您將更深入地理解漸進式圖片加載技術的優勢。

用戶體驗

用戶體驗是現代網站設計的核心要素之一,它直接影響著網站的可訪問性、可使用性和可喜悅性。在設計過程中,我們需要考慮多個方面,包括視覺美感、操作便捷性、交互體驗等,以確保每一位訪客都能享受到優質的使用體驗。

Content Layout Shift (CLS)

Content Layout Shift (CLS) 是衡量網頁視覺穩定性的重要指標,它反映了頁面在加載過程中元素位置的變化程度。當頁面上的內容,如圖片、廣告或動態加載的元素,在沒有用戶交互的情況下突然改變位置,就會發生 CLS。這種意外的移動不僅會干擾用戶的閱讀體驗,還可能導致用戶點擊錯誤的位置,造成操作失誤。

CLS 的產生有多種原因,最常見的包括:未設定尺寸的圖片或影片、動態插入的內容、字體加載導致的文本重排,以及依賴網絡響應的元素(如廣告)。這些元素在加載完成後可能會推擠周圍的內容,導致整個頁面布局發生變化。對於用戶來說,這種體驗就像是在閱讀一篇文章時,文字和圖片突然跳動,迫使他們重新尋找閱讀位置,這無疑會增加用戶的挫折感。

優化 CLS 對於提升網站的用戶體驗和搜索引擎排名都至關重要。Google 將 CLS 作為核心網頁指標之一,直接影響網站的 SEO 表現。通過採取一系列措施,如為圖片和媒體元素預留空間、使用占位符、優化字體加載等,可以顯著減少 CLS 的發生。這不僅能提高頁面的視覺穩定性,還能增強用戶對網站的信任度,最終提升整體的用戶滿意度和網站轉化率。

Large Contentful Paint (LCP)

Large Contentful Paint (LCP) 是衡量網頁性能的重要指標之一,它反映了頁面主要內容的加載時間。這個指標直接影響用戶對網站速度的感知,因為它測量的是視口內最大的內容元素(通常是圖片、視頻或大型文本塊)變得可見的時間。優化 LCP 不僅能提升用戶體驗,還能改善搜索引擎排名,因為搜索引擎越來越重視網頁的加載速度。

LCP 的優化涉及多個方面,包括服務器響應時間、資源加載順序、圖片優化等。例如,使用內容分發網絡(CDN)可以減少服務器響應時間;優先加載關鍵資源可以加快主要內容的呈現;而使用現代圖片格式和延遲加載技術則可以顯著改善大型圖片的加載時間。此外,減少不必要的 JavaScript 和 CSS,以及優化字體加載也是常見的 LCP 優化策略。

監測和改進 LCP 需要持續的努力和測試。開發者可以使用 Chrome 開發者工具、Lighthouse 或 Web Vitals 等工具來測量 LCP,並根據測試結果進行針對性優化。值得注意的是,LCP 的優化不應以犧牲內容質量為代價,而應該在保證豐富用戶體驗的同時,尋求更高效的內容呈現方式。通過平衡性能和功能,我們可以創造出既快速又吸引人的網頁體驗。

技術原理解析

下圖展示了 Progressive Image Loading 的完整流程:從 SVG 預佔位,到 BlurHash、LQIP,再漸進式切換至高品質圖片。

使用 SVG 來避免 CLS

當我們談論圖片載入時,常常關注畫質與壓縮技術,但實際上,圖片「尚未載入時」的版面表現同樣重要。這正是 SVG(Scalable Vector Graphics)在漸進式圖片載入中扮演關鍵角色的原因之一。透過嵌入式 SVG,我們能在圖片尚未出現前,就先行為它預留出正確的空間比例,從而有效防止 CLS(Cumulative Layout Shift)等常見的排版問題發生。這種「預佔位」策略不但提升視覺穩定性,也讓使用者從進入頁面起就感受到更流暢的體驗。

SVG 的設計彈性使其特別適合用於圖片容器。藉由 viewBox 屬性與 preserveAspectRatio 設定,我們可以準確控制圖片在不同解析度下的顯示比例。這段範例程式碼展示了如何以 <svg> 建立一個響應式容器,搭配 width="100%"height="${height}" 的設計,確保容器在頁面載入初期就能正確佔位,而不會干擾整體版面。內部的 <rect> 元素還能提供中性色背景,讓使用者不會面對一片空白的畫面。這些看似簡單的設定,其實正是打造穩定、優雅圖片載入流程的基礎。

使用 BlurHash 提供即時視覺反饋

BlurHash 是一種創新的圖片預覽技術,它巧妙地將圖片的主要視覺特徵壓縮成一個簡短的字符串。這個字符串通常僅有 20-30 個字符長,卻能夠在客戶端被解碼並渲染成一個模糊但視覺上令人愉悅的預覽圖。這種技術不僅能夠大幅減少初始加載時間,還能為用戶提供即時的視覺反饋,極大地提升了網頁的感知性能。

BlurHash 的工作原理涉及多個步驟。首先,在服務器端,系統會分析原始圖片,提取其主要的色彩和形狀特徵。這些特徵隨後被編碼成一個簡潔的字符串,例如 LlNA}44TN{kqyEtls:xux^tRRjRi。當這個字符串傳輸到客戶端後,前端程序會將其解碼成像素數據。最後,這些像素數據通過 Canvas 技術被渲染成一個模糊的預覽圖,為用戶呈現出原圖的大致輪廓和色彩分布。

相比於傳統的圖片加載方式,BlurHash 具有多項顯著優勢。首先,它的傳輸成本極低,通常只需要約 30 字節的數據量,這大大減少了網絡負載。其次,儘管數據量小,BlurHash 仍能保留並呈現原圖的主要色彩分布,為用戶提供更加豐富的視覺體驗。最後,與簡單的單色佔位符相比,BlurHash 生成的預覽圖在視覺效果上更為優越,能夠更好地吸引用戶注意力並改善整體的頁面美感。這些特性使得 BlurHash 成為現代網頁設計中提升用戶體驗的有力工具。

使用 LQIP 提供更佳的視覺體驗

LQIP (Low Quality Image Placeholders) 是一種優化圖片載入體驗的技術,通過先顯示極低解析度的縮略圖,再在原圖載入完成後無縫替換,來提升用戶的視覺體驗。這種方法的核心在於從原圖生成超小尺寸(如 20x15px)的縮略圖,將其壓縮並轉換為 Base64 格式或使用 WebP 格式內嵌,然後在頁面初始載入時顯示這個 LQIP,同時在背景加載高品質原圖,最後在原圖加載完成後實現平滑過渡。

LQIP 與 BlurHash 相比,各有其優勢。LQIP 的檔案大小通常比 BlurHash 大(約 300-600B),但其視覺細節可能更為豐富。此外,LQIP 的實作相對更為簡單,不需要額外的解碼步驟,這使得它在某些場景下更容易被採用和實現。

然而,選擇使用 LQIP 還是 BlurHash 取決於具體的應用場景和需求。LQIP 可能更適合那些需要更多細節預覽的場景,而 BlurHash 則可能在極小檔案大小和快速載入方面有優勢。無論選擇哪種技術,兩者的目標都是相同的:在保證頁面快速加載的同時,為用戶提供更好的視覺體驗,減少等待高品質圖片載入時的視覺空白。

載入高品質圖片

在漸進式圖片載入過程中,我們如同藝術家創作一幅精美畫作。首先,SVG 勾勒出畫布輪廓,確保圖片容器比例恆定。BlurHash 或 LQIP 技術隨後快速塗抹模糊底色,為用戶描繪圖像大致形態與氛圍,宛如畫家的即時速寫,捕捉主題精髓。

高品質圖片逐步載入,細節逐一填充,恍若藝術家精心雕琢每一筆觸。這種漸進式載入不僅優化了性能,更為用戶呈現出層次豐富的視覺盛宴,彷彿欣賞一幅逐步完善的藝術品。

這種創新的圖片載入策略不僅解決了技術難題,更將藝術美學融入了網頁設計之中。它巧妙平衡了性能優化與視覺體驗,為用戶帶來既快速又優雅的瀏覽感受。通過這種方法,我們成功將枯燥的圖片加載過程轉化為一場引人入勝的視覺饗宴,展現了技術與藝術的完美結合。

實作流程與程式碼範例

讓我們使用 Vue 3 和 Astro 框架實作完整的漸進式圖片載入組件。

圖片預處理

首先,需要為每張圖片生成 BlurHash 或 LQIP。以下是常見的處理方法:

後端生成 BlurHash

// Node.js 環境中使用 blurhash 套件
import { encode } from "blurhash";
import sharp from "sharp";

async function generateBlurhash(imagePath) {
  // 讀取圖片
  const { data, info } = await sharp(imagePath)
    .raw()
    .ensureAlpha()
    .toBuffer({ resolveWithObject: true });

  // 生成 BlurHash
  const hash = encode(
    new Uint8ClampedArray(data),
    info.width,
    info.height,
    4, // x 組件數
    3 // y 組件數
  );

  return {
    blurhash: hash,
    width: info.width,
    height: info.height,
  };
}

// 使用方式
const metadata = await generateBlurhash("path/to/image.jpg");
// { blurhash: "LlNA}44TN{kqyEtls:xux^tRRjRi", width: 800, height: 600 }

使用 Vite 插件生成 LQIP

對於使用 Vite 作為建置工具的專案,可以直接使用 vite-plugin-lqip 插件:

// vite.config.js
import lqip from "vite-plugin-lqip";

export default {
  plugins: [lqip()],
};

// 使用方法
import imgData from "./path/to/image.jpg?lqip";
// imgData = { lqip: "base64...", src: "/assets/image.jpg", width: 800, height: 600 }

前端組件實作 - Vue 3

以下是一個完整的 Vue 3 漸進式圖片載入組件:

<template>
  <div
    class="progressive-image"
    :style="{ paddingBottom: `${(height / width) * 100}%` }"
  >
    <!-- SVG 容器,確保比例一致 -->
    <svg
      v-if="!loadedLqip && !loadedHighQuality"
      viewBox="0 0 width height"
      preserveAspectRatio="none"
      :width="width"
      :height="height"
    ></svg>

    <!-- BlurHash 預覽 (通過 Canvas 渲染) -->
    <canvas
      v-if="useBlurHash && blurHash && !loadedHighQuality"
      ref="blurHashCanvas"
      class="blur-preview"
      :class="{ 'fade-out': loadedHighQuality }"
    ></canvas>

    <!-- LQIP 預覽 -->
    <img
      v-if="lqipSrc && !useBlurHash && !loadedHighQuality"
      :src="lqipSrc"
      class="lqip-preview"
      :class="{ 'fade-out': loadedHighQuality }"
      alt=""
      @load="loadedLqip = true"
    />

    <!-- 高品質圖片 -->
    <img
      ref="highQualityImg"
      :data-src="src"
      :alt="alt"
      class="high-quality"
      :class="{ loaded: loadedHighQuality }"
      @load="onHighQualityLoaded"
    />
  </div>
</template>

<script setup>
import { ref, onMounted, watch } from "vue";
import { decode } from "blurhash";

const props = defineProps({
  src: { type: String, required: true },
  alt: { type: String, default: "" },
  width: { type: Number, required: true },
  height: { type: Number, required: true },
  lqipSrc: { type: String, default: "" }, // Base64 LQIP
  blurHash: { type: String, default: "" }, // BlurHash 字串
  useBlurHash: { type: Boolean, default: false }, // 是否使用 BlurHash
});

const loadedLqip = ref(false);
const loadedHighQuality = ref(false);
const blurHashCanvas = ref(null);
const highQualityImg = ref(null);
const observer = ref(null);

// 渲染 BlurHash 至 Canvas
const renderBlurHash = () => {
  if (!props.blurHash || !blurHashCanvas.value) return;

  const canvas = blurHashCanvas.value;
  const ctx = canvas.getContext("2d");
  const canvasWidth = 32; // 低解析度即可
  const canvasHeight = Math.floor(canvasWidth * (props.height / props.width));

  canvas.width = canvasWidth;
  canvas.height = canvasHeight;

  // 解碼 BlurHash 字串為像素資料
  const pixels = decode(props.blurHash, canvasWidth, canvasHeight);
  const imageData = new ImageData(pixels, canvasWidth, canvasHeight);
  ctx.putImageData(imageData, 0, 0);
};

// 監聽圖片進入視口
const setupIntersectionObserver = () => {
  if (!highQualityImg.value) return;

  observer.value = new IntersectionObserver(
    (entries) => {
      const entry = entries[0];
      if (entry.isIntersecting) {
        // 圖片進入視口,開始載入高品質圖片
        highQualityImg.value.src = highQualityImg.value.dataset.src;
        observer.value.disconnect();
      }
    },
    { rootMargin: "200px" }
  ); // 預載邊界

  observer.value.observe(highQualityImg.value);
};

const onHighQualityLoaded = () => {
  loadedHighQuality.value = true;
};

onMounted(() => {
  if (props.useBlurHash) {
    renderBlurHash();
  }
  setupIntersectionObserver();
});

// 當 BlurHash 或 Canvas 引用變化時重新渲染
watch(() => [props.blurHash, blurHashCanvas.value], renderBlurHash);
</script>

<style scoped>
.progressive-image {
  position: relative;
  width: 100%;
  overflow: hidden;
  background-color: #f0f0f0;
}

.blur-preview,
.lqip-preview,
.high-quality {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  transition: opacity 0.5s ease;
}

.blur-preview {
  filter: blur(20px);
  transform: scale(1.1); /* 彌補模糊邊緣 */
}

.lqip-preview {
  object-fit: cover;
  filter: blur(10px);
  transform: scale(1.05);
}

.high-quality {
  opacity: 0;
  object-fit: contain;
}

.high-quality.loaded {
  opacity: 1;
}

.fade-out {
  opacity: 0;
}
</style>

Astro 框架實作

在 Astro 中,可以結合動態圖片導入和上述 Vue 組件:

---
// ProgressiveImage.astro
import { Image } from 'astro:assets';
import ProgressiveImageVue from './ProgressiveImage.vue';
import { getImageAsBuffer } from '../utils/image';

const { src, alt, width, height, blurhash } = Astro.props;

// 動態導入圖片
let imageData;
if (typeof src === 'string') {
  // 遠程圖片,使用提供的 blurhash
  imageData = {
    src,
    blurhash,
    width,
    height,
    useBlurHash: true
  };
} else {
  // 本地圖片,可以使用 LQIP 或 BlurHash
  try {
    // 如果使用 vite-plugin-lqip
    const lqipData = await import(`${src.src}?lqip`);
    imageData = {
      src: src.src,
      lqipSrc: lqipData.default.lqip,
      width: lqipData.default.width,
      height: lqipData.default.height
    };
  } catch (e) {
    // 回退至標準圖片
    imageData = {
      src: src.src,
      width: width || src.width,
      height: height || src.height
    };
  }
}
---

<ProgressiveImageVue
  client:load
  src={imageData.src}
  lqipSrc={imageData.lqipSrc}
  blurHash={imageData.blurhash}
  useBlurHash={imageData.useBlurHash}
  width={imageData.width}
  height={imageData.height}
  alt={alt}
/>

使用方式:

---
// 頁面中使用
import ProgressiveImage from '../components/ProgressiveImage.astro';
import heroImage from '../assets/hero.jpg';
---

<ProgressiveImage
  src={heroImage}
  alt="文章首圖"
  blurhash="LlNA}44TN{kqyEtls:xux^tRRjRi"
/>

成效與比較

用戶體驗對比

傳統載入流程通常包括白屏佔位、突然顯示完整圖片或逐行載入,這可能導致頁面跳動,影響用戶體驗。這種方式不僅視覺效果不佳,還可能造成內容佈局的不穩定。

相比之下,漸進式載入流程提供了更優雅的解決方案。它首先立即顯示色塊或模糊預覽,確保圖片佈局穩定且無跳動,然後通過平滑過渡將預覽圖像轉換為高清圖片。這種方法不僅提高了頁面的加載速度感知,還大大改善了整體用戶體驗。

結語

透過結合 SVG、BlurHash 與 LQIP 技術,我們能夠大幅提升網站的圖片載入體驗,帶來更好的用戶體驗和更高的效能指標。這種漸進式圖片載入策略尤其適合文章首圖、產品圖庫、Banner 與輪播,以及相片牆等場景。這些場景通常涉及大量高品質圖片或對頁面載入速度有顯著影響的視覺元素,通過優化載入方式可以有效提升整體用戶體驗。

為進一步優化圖片載入體驗,我們可以考慮與 AVIF 和 WebP 等新一代圖片格式結合,利用自適應圖片 CDN 根據設備與網路條件動態提供最佳圖片,並充分運用 <picture> 元素來靈活選擇最佳圖片格式與解析度。同時,採用 HTTP/3 協議和 Preload 機制可以進一步提升網路傳輸效率和資源預載能力,為用戶帶來更快速、更流暢的瀏覽體驗。

對於使用現代前端框架的開發者,將這些技術整合到開發工作流中至關重要。Astro 用戶可以利用其內建的圖片優化功能,Vue 和 React 開發者可以封裝可重用的漸進式圖片組件,而 Vite 用戶則可以使用 vite-plugin-lqip 自動生成 LQIP。此外,將圖片處理整合到 CI/CD 流程中,可以實現預覽圖片資源的自動生成。通過這些工程化手段和工具整合,我們不僅能顯著提升網站的效能指標,還能為用戶創造更加愉悅的視覺體驗,使圖片從潛在的性能瓶頸轉變為增強用戶體驗的關鍵元素。

技術文章 前端開發 網站效能 SEO
關於 Calpa

Calpa 擅長使用 TypeScriptReact.jsVue.js 建立 Responsive Website。

他積極參與開源社區,曾在 2019 年的香港開源大會上擔任講者,提供工作經驗和見解。此外,他也在 GitHub 上公開分享個人博客程式碼,已獲得超過 300 顆星星和 60 個分支的支持。

他熱愛學習新技術,並樂意分享經驗。他相信,唯有不斷學習才能跟上快速演變的技術環境。

熱門文章

最新文章

圖片管理中心
管理圖片資源
IP 查詢
快速查詢和定位 IP 地址的地理位置和相關信息
Python 運行器
無需後端、無需登入,只需打開瀏覽器即可運行 Python 代碼(由 Pyodide 提供支持)
封面圖生成器
自動創建適合各種平台的文章封面圖
原作(青山剛昌)產生器
一鍵創建原作(青山剛昌)的封面圖
日本色彩
探索和使用傳統日本色彩
部落格內容洞察儀表板
以視覺化儀表板方式追蹤文章成效、分享熱度與分類分布,協助創作者掌握內容表現。
蒙特卡羅估算 π
使用蒙特卡羅方法演示 π 值的估算過程
LLM
使用 LLM 模型進行聊天
活動圖生成器
一鍵創建活動的封面圖
Wagmi Card
一鍵創建 Wagmi 的封面圖
Facebook Quote
Facebook Quote
Music Macro Language (MML) Studio
用程式語法編寫旋律,用音符構築想像
Blurhash
一鍵創建 Blurhash
文字分類器
使用 MediaPipe TextClassifier 分類文字
前端工程師免費工具資源
前端工程師免費工具資源
後端工程師免費工具資源
後端工程師免費工具資源
全端工程師免費工具資源
全端工程師免費工具資源
Web3 工程師免費工具資源
Web3 工程師免費工具資源
紫微斗數排盤系統|結合 AI 的命盤性格與事業財務分析生成器
紫微斗數排盤工具,輸入生日與時辰,自動生成完整命盤分析提示(Prompt)。結合最專業紫微理論與 AI 助力,助你深入解析性格、事業、財務與人際課題。免費使用,適合命理師及紫微愛好者。
PixAI Prompt 組合器|快速打造可用於 AI 繪圖的語言拼圖
使用 PixAI 卻不會寫 prompt?這個工具幫你一鍵組裝角色、表情、風格語彙,輸出高品質繪圖提示語句(Prompt),可直接貼入 PixAI 使用。適合插畫師、創作者、AI 新手與 VTuber 角色開發者。