在 Next.js 10 版以後發佈了 <Image />
這個 component,是由 Google Chrome Team 幫他們建造的,主要用來解決圖片載入效能的問題。
<Image />
component 使用起來非常地方便,只要先從 next/image
後即可開箱使用:
import Image from "next/image";
function Home() {
return (
<>
<h1>My Homepage</h1>
<Image
src="/me.png"
alt="Picture of the author"
width={500}
height={500}
/>
<p>Welcome to my homepage!</p>
</>
);
}
export default Home;
<Image />
元件解決的問題<Image />
解決的效能問題可以分成 5 個面向來討論:
width
與 height
有一項問題是圖片是 2000x2000 的圖片時怎麼辦?在手機版的網頁中,我們不希望讓使用者載入過大的圖片檔案。要根據使用者的裝置大小載入合適的圖片,可以使用 srcset
這個 <img />
的屬性:
<img srcset="elva-fairy-480w.jpg 480w,
elva-fairy-800w.jpg 800w"
sizes="(max-width: 600px) 480px,
800px"
src="elva-fairy-800w.jpg"
alt="Elva dressed as a fairy">
而如果要在網站中使用 srcset
這個屬性,最大的問題是要如何有效地生成不同大小的圖片,而且對於工程師來說不會造成負擔。以往要做到這件事需要很多的設定,例如使用 webpack、gulp 等工具對圖片進行處理。
而在 Next.js 中的 <Image />
元件已經幫我們做掉這件事,幾乎不用設定就可以讓網頁可以根據使用者的裝置大小載入合適的圖片。
現今有許多較新的圖片檔案格式比較久遠的圖片格式擁有更小的檔案大小,例如 WebP、AVIF,而 WebP 比 JEPG 的檔案格式甚至少了 30%。如果想要根據使用者的瀏覽器判斷要用哪種格式的圖片,在原生的 HTML 中也是可以使用 srcset
這個 <img />
的屬性解決。
最麻煩的地方同樣在於要設定自動化轉換圖片的程式,實際上 <Image />
也包含自動轉換圖片格式的功能,我們不必再做其他設定,預設就會根據使用者的瀏覽器給予 WebP 或原生的圖片格式。
對於使用者來說瀏覽一個畫面載入越少資源越好,因為關係到使用者能夠更快速看到頁面中顯示第一個像素 (First Contentful Paint),此外越少資源代表 Large Contentful Paint 的評分也就好。換句話說,使用者在瀏覽頁面時實際上只需優先載入看到的部分 (viewport),看不到的部分可能等待使用者滾動畫面,接近該區塊時才載入圖片,藉此讓使用者不必在一開始就載入所有的資源。
Next.js 將 CSS 與 JavaScript 視為比圖片有更高優先權,但是有些情況例外,像是在 landing page,在最上面的 viewport 有一些圖片,按照 <Image />
預設的設定會是 lazy loading,因此使用者在載入頁面後需要等待一段時間才會看到圖片。
我們會希望 Large Contentful Paint 可以有更好的分數,所以想要提高圖片的載入優先度,一般的做法會使用 <link rel="preload">
,可是如果有很多張圖片都需要加入這個設定到 <head>
不免讓程式碼不好看, <Image />
可以讓工程師更簡地設置屬性,設定 priority={true}
讓 Next.js 幫我們自動生成 preload 的 <link>
。
<Image
src="/me.png"
alt="Picture of the author"
width={500}
height={500}
priority={true}
/>
width
與 height
heigth
跟 width
是很重要的屬性,有設定圖片的長跟寬才能讓瀏覽器避免在載入時不會 reflow,因此降低 Cumulative layout shift (CLS) 的分數。
<Image>
component 也需要設置 height
跟 width
,但是這兩個屬性並不會直接影響圖片顯示的長跟寬,而是用來決定圖片的比例。
也許你會想到一個問題時,像是電商網站有數以萬計的圖片,難道要在打包時處理大量的圖片轉換嗎?這個跟 Incremental Static Regeneration 很相似,都是在使用者瀏覽網頁時才會動態地處理,不會在打包時一口氣處理大量的檔案,這是非常費時的一件事情。