建立有效的圖像元件

圖片元件內含效能最佳做法,並提供立即可用的圖片最佳化解決方案。

Leena Sohoni
Leena Sohoni
Kara Erickson
Kara Erickson
Alex Castle
Alex Castle

圖片是網頁應用程式效能瓶頸的常見來源,也是最佳化的重點領域。未經最佳化的圖片會導致網頁膨脹,在 90th 百分位數時,占總網頁重量的 70% 以上。有多種方法可針對圖片進行最佳化,其中包含智慧型「圖片元件」,並預設內建效能解決方案。

Aurora 團隊與 Next.js 合作,建構這類元件。我們的目標是建立最佳化的圖片範本,方便網頁程式開發人員進一步自訂。這個元件不僅是良好的模型,也是設定其他架構、內容管理系統 (CMS) 和技術堆疊圖片元件的標準。我們目前合作開發了 Nuxt.js 相關元件,並正與 Angular 合作,共同改善日後版本的圖片最佳化功能。本文將討論我們如何設計 Next.js 圖片元件,以及過程中學到的經驗。

圖片元件做為圖片的擴充功能

圖片最佳化問題和商機

圖片不僅會影響成效,還會影響商機。網頁中的圖片數量是網站訪客的第二位轉換預測工具。使用者完成轉換的工作階段所含圖片比未完成轉換的工作階段少了 38%。Lighthouse 會列出多項最佳化圖片和改善網站重要指標商機,做為最佳做法稽核的一部分。以下列舉一些圖片對核心網站體驗核心指標影響,以及使用者體驗的一些常見狀況。

未調整大小的圖片可能會損害 CLS

如果未指定圖片的大小,可能會導致版面配置不穩定,並導致累計版面配置位移 (CLS) 過高。在 img 元素上設定 widthheight 屬性,有助於避免版面配置位移。例如:

<img src="flower.jpg" width="360" height="240">

寬度和高度應設為讓算繪圖片的顯示比例接近其自然顯示比例。顯示比例的差異可能會導致圖片變形。這是一種相當新的屬性,可讓您 在 CSS 中指定 aspect-ratio,不僅可因應圖片自動調整大小,也能防止 CLS。

大型圖片可能會影響 LCP

圖片檔案越大,下載時間就越長。大型圖片可能是網頁的「主圖片」,或是可視區域中負責觸發最大內容繪製 (LCP) 的最重要元素。是重要內容的一部分,但下載時間過長,導致 LCP 延遲。

在許多情況下,開發人員可以使用更好的壓縮和回應式圖片降低圖片大小。<img> 元素的 srcsetsizes 屬性可用來提供不同大小的圖片檔案。瀏覽器會根據螢幕大小和解析度選擇適當的圖片。

圖片壓縮品質不佳可能會對 LCP 造成負面影響

AVIFWebP 等新型圖片格式的壓縮效果比常用的 JPEG 和 PNG 格式更好。在某些情況下,使用較佳的壓縮方式可將檔案大小縮減 25% 至 50%,同時維持相同的圖片品質。縮減大小可加快下載速度,同時減少數據用量。應用程式應為支援這些格式的瀏覽器提供新型圖片格式

載入不必要的圖片會損害 LCP

網頁載入時,系統不會向使用者顯示折疊下方或不在可視區域內的圖片。可以延遲,以免造成 LCP 延遲並延遲。您可以使用延遲載入功能,在使用者捲動畫面時載入這類圖片。

最佳化難題

團隊可以評估上述問題造成的成效成本,並導入最佳做法解決方案來克服這些問題。不過,這種情況在實際操作中通常不會發生,而且效率不佳的圖片會持續拖慢網頁速度。可能原因如下:

  • 優先順序:網頁開發人員通常會著重於程式碼、JavaScript 和資料最佳化。因此他們可能不知道圖片問題,或如何調整圖片。設計師建立或使用者上傳的圖片,可能不是優先處理項目。
  • 現成解決方案:即使開發人員瞭解圖片最佳化作業的細微差異,但如果沒有適用於架構或技術堆疊的現成解決方案,可能會讓他們卻步不前。
  • 動態圖片:除了應用程式中的靜態圖片外,動態圖片是由使用者上傳,或來自外部資料庫或內容管理系統 (CMS)。在圖片來源為動態圖片的情況下,定義這類圖片的大小可能會相當困難。
  • 標記超載:如要加入圖片大小或 srcset 適用於不同大小的解決方案,則需要為每張圖片額外標記,這可能太麻煩了。srcset 屬性是在 2014 年推出,但目前只有 26.5% 的網站使用這項屬性。使用 srcset 時,開發人員必須建立各種尺寸的圖片。just-gimme-an-img 等工具可以提供協助,但必須手動為每張圖片使用。
  • 瀏覽器支援:AVIF 和 WebP 等新型圖片格式可建立較小的圖片檔案,但在未支援這些格式的瀏覽器上需要特殊處理。開發人員必須採用內容協商<picture> 元素等策略,才能將圖片提供給所有瀏覽器。
  • 延遲載入的複雜性:有多種技巧和程式庫可用於為下捲動畫面中的圖片實作延遲載入。選擇最合適的選項可能會很困難。開發人員也可能不知道從「折疊」載入延後圖片的最佳距離。裝置的可視區域大小不同,可能會使這項作業更加複雜。
  • 改變橫向模式:由於瀏覽器開始支援新的 HTML 或 CSS 功能來提升效能,開發人員可能難以逐一評估各項功能。舉例來說,Chrome 會推出擷取優先順序功能,做為原始來源試驗。可用於提升網頁上特定圖片的優先順序。整體而言,開發人員可以更容易在元件層級評估及實行這類改善項目。

圖片元件做為解決方案

我們發現圖片最佳化有許多機會,但為每個應用程式個別實作這些機會時,會遇到許多挑戰,因此我們決定推出圖片元件。圖片元件可封裝並強制執行最佳做法。開發人員可以將 <img> 元素替換為圖片元件,以便更妥善地解決圖片效能問題。

過去一年來,我們與 Next.js 架構合作設計及實作圖片元件。它可用於取代 Next.js 應用程式中現有的 <img> 元素,如下所示。

// Before with <img> element:
function Logo() {
  return <img src="/logo.jpg" alt="logo" height="200" width="100" />
}

// After with image component:
import Image from 'next/image'

function Logo() {
  return <Image src="/logo.jpg" alt="logo" height="200" width="100" />
}

這項元件嘗試運用豐富的功能與原則,以一般的方式解決圖片相關問題。開發人員也可以根據不同的圖片規定,自訂這個套件。

防止版面配置位移

如前所述,未大小的圖片會導致版面配置位移,並造成 CLS。使用 Next.js 圖片元件時,開發人員必須使用 widthheight 屬性提供圖片大小,以免版面配置出現偏移。如果尺寸不明,開發人員必須指定 layout=fill,才能提供位於大小尺寸容器中且未調整大小的圖片。您也可以使用靜態圖片匯入功能,在建構期間擷取硬碟上實際圖片的大小,並將其納入圖片中。

// Image component with width and height specified
<Image src="/logo.jpg" alt="logo" height="200" width="100" />

// Image component with layout specified
<Image src="/hero.jpg" layout="fill" objectFit="cover" alt="hero" />

// Image component with image import
import Image from 'next/image'
import logo from './logo.png'

function Logo() {
  return <Image src={logo} alt="logo" />
}

由於開發人員無法使用未調整圖片元件的大小,因此這項設計可確保他們會花時間考慮圖片大小,並避免版面配置位移。

提升回應速度

如要讓圖片在各裝置上顯示回應式效果,開發人員必須在 <img> 元素中設定 srcsetsizes 屬性。我們希望透過 Image 元件減少這項工作。我們設計 Next.js 圖片元件時,將每個應用程式只設定一次屬性值。我們會根據版面配置模式,將這些屬性套用至所有 Image 元件例項。我們提出了三個解決方案:

  1. deviceSizes 屬性:這項屬性可用於根據應用程式使用者群常見的裝置,一次性設定中斷點。設定檔中包含中斷點的預設值。
  2. imageSizes 屬性:這也是可設定的屬性,用於取得與裝置大小中斷點相對應的圖片大小。
  3. 每張圖片中的 layout 屬性:用於指出如何使用每張圖片的 deviceSizesimageSizes 屬性。支援的版面配置模式值為 fixedfillintrinsicresponsive

當以「回應式」或「填入」版面配置模式要求圖片時,Next.js 會根據要求網頁的裝置大小識別要提供的圖片,並適當設定圖片中的 srcsetsizes

下列比較圖顯示如何使用版面配置模式,控制不同螢幕上的圖片大小。我們使用了 Next.js 說明文件中提供的示範圖片,在手機和標準筆記型電腦上查看。

筆電螢幕 手機螢幕
Layout = Intrinsic:縮小至配合較小可視區域的容器寬度。在較大的可視區域中,不會將圖片放大到超過圖片內在大小的程度。容器寬度為 100%
顯示原始的山脈圖片 山地圖片縮小
版面配置 = 固定:圖片無法回應。無論在哪部裝置上算繪,寬度和高度都會固定為類似於 `` 元素的值。
山岳影像與原圖 顯示的山脈圖片不符合螢幕大小
版面配置 = 回應式:根據不同可視區域中的容器寬度縮小或放大,並維持顯示比例。
已將山中圖片放大至符合螢幕尺寸 縮小山脈圖片以符合螢幕大小
版面配置 = 填滿:寬度和高度會拉伸,以填滿父項容器。(此範例中的父項 <div> 寬度設為 300*500)
經過算繪的山脈圖片,尺寸為 300 x 500 經過算繪的山脈圖片,尺寸為 300 x 500
針對不同版面配置算繪的圖片

提供內建的延遲載入功能

Image 元件提供內建的高效能延遲載入解決方案。使用 <img> 元素時,有幾個延後載入選項,但都有一些缺點,因此不易使用。開發人員可以採用下列其中一種延遲載入做法:

  • 指定 loading 屬性:所有新式瀏覽器都支援這項功能
  • 使用 Intersection Observer API:建構自訂延遲載入解決方案需要付出努力,並進行周全的設計和實作。但開發人員不一定有時間這麼做。
  • 匯入第三方程式庫以延遲載入圖片:可能需要進行額外的操作,才能評估和整合合適的第三方程式庫,以便延遲載入。

在 Next.js 圖片元件中,載入作業預設為 "lazy"。延遲載入功能是透過 Intersection Observer 實作,大多數新式瀏覽器都支援。開發人員無須採取額外設定即可啟用,但可以視需要停用。

預先載入重要圖片

LCP 元素通常是圖片,而大型圖片可能會延遲 LCP。建議您預先載入重要圖片,讓瀏覽器能更快發現該圖片。使用 <img> 元素時,HTML 標頭可能會包含預先載入提示,如下所示。

<link rel="preload" as="image" href="important.png">

無論使用哪個架構,設計良好的圖片元件都應提供調整圖片載入順序的方法。在 Next.js 圖片元件方面,開發人員可以使用圖片元件的 priority 屬性,指出適合預先載入的圖片。

<Image src="/hero.jpg" alt="hero" height="400" width="200" priority />

新增 priority 屬性可簡化標記使用起來,因此更便捷。圖片元件開發人員也可以探索應用程式選項,以便套用啟發式搜尋,自動預先載入符合特定條件的頁面上方圖片。

鼓勵代管高效能圖片

建議使用圖片 CDN 自動最佳化圖片,而且還支援 WebP 和 AVIF 等新型圖片格式。Next.js 圖片元件預設會使用載入器架構的圖片 CDN。以下範例顯示載入器允許在 Next.js 設定檔中設定 CDN。

module.exports = {
  images: {
    loader: 'imgix',
    path: 'https://ImgApp/imgix.net',
  },
}

透過這項設定,開發人員可以使用圖片來源中的相對網址,架構就會串連相對網址和 CDN 路徑,以產生絕對網址。支援 ImgixCloudinaryAkamai 等熱門圖片 CDN。這個架構可讓您為應用程式導入自訂 loader 函式,支援使用自訂雲端服務供應商。

支援自行託管的圖片

在某些情況下,網站可能無法使用圖片 CDN。在這種情況下,圖片元件必須支援自行代管的圖片。Next.js 圖片元件會使用圖片最佳化工具做為內建圖片伺服器,提供類似 CDN 的 API。如果已在伺服器上安裝 Sharp,最佳化工具就會使用 Sharp 進行生產映像檔轉換。對於想要自行建構圖片最佳化管道的使用者來說,這個程式庫是個不錯的選擇。

支援漸進式載入

漸進式載入是一種技術,可在實際圖片載入期間顯示預留位置圖片 (通常畫質較差),藉此維持使用者興趣。這有助於提升效能,並改善使用者體驗。您可以搭配延遲載入功能,為下方或上方圖片使用。

Next.js 圖片元件可透過 placeholder 屬性支援圖片的漸進式載入功能。這可做為 LQIP (低品質圖片預留位置),在實際圖片載入期間顯示低品質或模糊的圖片。

影響

在整合所有最佳化功能後,我們已成功在實際環境中使用 Next.js 圖片元件,並且也與其他技術堆疊合作,處理類似的圖片元件。

Leboncoin 將舊版 JavaScript 前端遷移至 Next.js 時,也升級了映像檔管道,以便使用 Next.js 映像檔元件。在從 <img> 遷移至 next/image 的網頁上,LCP 從 2.4 秒降至 1.7 秒。頁面下載的圖片位元組總數從 663 KB 降至 326 KB (延遲載入的圖片位元組約為 100 KB)。

汲取的經驗教訓

任何建立 Next.js 應用程式的人,都可以透過使用 Next.js 圖片元件來進行最佳化。不過,如果您想為其他架構或內容管理系統建立類似的效能抽象概念,以下是我們在過程中學到的幾個實用教訓。

安全閥可能弊大於利

搶先體驗 Next.js Image 元件時,我們提供了 unsized 屬性,讓開發人員略過大小規定,並使用未指定尺寸的圖片。我們認為,在無法事先得知圖片高度或寬度的情況下,這項功能就非常實用。不過,我們發現使用者在 GitHub 問題中推薦 unsized 屬性,做為解決大小限制要求問題的萬用解決方案,即使他們可以以不會惡化 CLS 的方式解決問題,也一樣如此。我們隨後淘汰並移除了 unsized 屬性。

區分有用阻力和無用阻力

圖片大小的規定就是「有益阻力」的例子。它會限制使用元件,但這樣做在交換後可帶來最大的效能優勢。如果使用者清楚瞭解潛在的效能優勢,就會樂於接受限制。因此,在說明文件和其他元件相關的已發布內容中,說明這項權衡是值得的。

不過,您可以找到解決方法,在不犧牲效能的情況下解決這類問題。舉例來說,在開發 Next.js 圖片元件時,我們收到客戶抱怨,表示查詢本機儲存圖片的大小很麻煩。我們新增了靜態圖片匯入功能,使用 Babel 外掛程式自動擷取本機圖片的尺寸,藉此簡化這項程序。

在便利功能和效能最佳化之間取得平衡

如果圖片元件只會對使用者施加「實用阻力」,開發人員通常不會想使用。我們發現,調整圖片大小及自動產生 srcset 值等效能功能仍是最重要的關鍵。開發人員專用的便利功能,例如自動延遲載入和內建模糊預留位置,也讓 Next.js 圖片元件受到關注。

制定功能藍圖,以便推動採用

要打造出適用於所有情況的完美解決方案非常困難。您可能會設計出 75% 使用者都能順利操作的內容,然後告訴其他 25% 的使用者:「在這些情況下,這個元件不適合您使用。」

實務上,這項策略實際上可能與組裝設計者不同,您希望開發人員採用元件,以便享有應用程式效能帶來的優勢。如果有使用者無法遷移,並且覺得自己無法參與對話,就很難實現這點。他們可能會表達失望,導致負面觀感,進而影響採用率。

建議您為元件建立藍圖,以便長期涵蓋所有合理的用途。在說明文件中明確說明不支援的項目和原因,有助於使用者瞭解元件要解決的問題。

結論

圖片使用和最佳化相當複雜,開發人員必須在圖片成效和品質之間取得平衡,同時確保優質的使用者體驗。因此,圖片最佳化作業的成本高昂且效率極高。

我們不希望每個應用程式都得重新發明輪子,因此設計了最佳做法範本,供開發人員、架構和其他技術堆疊在實作時參考使用。當我們支援其他架構的圖片元件時,這項經驗確實會證明其價值。

Next.js 圖片元件已成功改善 Next.js 應用程式的效能,進而提升使用者體驗。我們認為這項模型非常實用,可在更廣泛的生態系統中發揮效用。如果有開發人員想在專案中採用這項模型,我們很樂意聽取他們的意見。