Điểm số của WebVitals cho ứng dụng hay website của bạn là những thước đo rất quan trọng. Nó thể hiện việc trải nghiệm của người dùng có tốt hay không. Hơn nữa, điểm số này cũng ảnh hưởng đến việc xếp hạng của Google Search

Dưới đây là 4 điểm tối ưu mà next/image cung cấp khi được sử dụng 1 cách đúng đắn:

  • Improved Performance ⚡️: hiển thị ảnh có kích thước phù hợp cho từng thiết bị sử dụng những dạng ảnh hiện đại (webp, avif)
  • Visual Stability ⛷️: tự động ngăn chặn các lỗi CLF.
  • Faster Page Loads 🛸: chỉ load ảnh khi chúng nằm trong viewport, sử dụng blur placeholder
  • Asset Flexibility 🦢: thay đổi kích thước hình ảnh theo yêu cầu, kể cả với những ảnh được đặt ở remote server

Giải thích những ưu điểm - Dài lắm đừng đọc 👽 👼

Cải thiện hiệu suất - Improved performance ⚡️

Để tăng hiệu suất cho ứng dụng của bạn, next/image tự động sử dụng các định dạng ảnh như .webp hoặc .avif nếu nó được hỗ trợ

Định dạng ảnh .webp đc sử dụng
Định dạng ảnh .webp đc sử dụng (Xem ảnh gốc)

Ngoài ra, nó cũng cung cấp khả năng tự động tạo ra các kích thước hình ảnh phù hợp cho từng thiết bị. Việc này được xử lý bởi loader. Mục đích chính là tăng tốc độ tải ảnh tùy thuộc vào thiết bị, màn hình

Tuy nhiên, việc tự động tạo kích thước ảnh chỉ làm việc khi bạn sử dụng layout=responsive hoặc layout=fill

4 chế độ layout chính
4 chế độ layout chính (Xem ảnh gốc)

Tăng sự ổn định về mặt hiển thị - Visual Stability ⛷️

Nghe có vẻ hơi trừu tượng 1 chút, để hình dung rõ hơn các bạn cần phải hiểu 1 chút về CLS - Cumulative Layout Shift

Ví dụ bạn có 1 nội dung HTML gôm 1 ảnh và 1 nội dung mô tả. Do bức ảnh không được chỉ định độ cao rõ ràng, nên sẽ gây ra hiện tượng nội dung mô tả bị đẩy thụt xuống sau khi bức ảnh được hiển thị. Đây được gọi là hiện tương CLS (dịch chuyển bố cục của trang)

Cumulative Layout Shift
Cumulative Layout Shift (Xem ảnh gốc)

Do đó, chúng ta luôn phải cung cấp cho ảnh 1 kích thước cụ thể. Hiểu rõ vấn đề này nên next/image cung cấp thuốc tính layout để giúp việc chỉ định kích thước

Tải trang nhanh hơn - Faster Page Loads 🛸

Tiếp theo, để tăng tốc độ tải trang nhanh hơn nữa, next/image không thực hiện hành động tải toàn bộ ảnh cùng 1 lúc. Thay vào đó, nó sẽ chỉ tải những ảnh đang nằm trong khu vực viewport đang hiển thị

Chỉ ảnh trong viewport đc hiển thị
Chỉ ảnh trong viewport đc hiển thị (Xem ảnh gốc)

Hơn nữa, để trải nghiệm của người dùng "mượt" hơn khi ảnh được tải xuống, thì next/image cung cấp thuộc tính placeholder.

Với ảnh đang được tải, thay vì hiển thị "bụp 1 cái" để "đe dọa người dùng" 😂 thì 1 hiệu ứng mờ ảo sẽ được hiện ra, rồi dần dần chuyển sang rõ nét khi ảnh đc tải xuống hoàn tất

Ảnh có sử dụng blur
Ảnh có sử dụng blur (Xem ảnh gốc)

Xử lý file linh hoạt - Asset Flexibility 🦢

Đôi khi thay vì lưu trữ hình ảnh trên cùng 1 máy chủ web thì chúng ta sử dụng dịch vụ lưu trữ của bên thứ 3. Với trường hợp này ta vẫn có thể sử dụng Image Optimization API

Để làm được việc này, thì bắt buộc bạn phải cung cấp danh sách các domain cần thiết. Các cài đặt này được thực hiện trong next.config.js

// next.config.js

module.exports = {
  images: {
    domains: ['example.com', 'example2.com'],
  },
}

Và nếu bạn còn lo lắng về việc "next/image có hỗ trợ tính năng tự động resize ảnh khi sử dụng dịch vụ lưu trữ bên thứ 3 hay không ?" thì, ... nó cũng hỗ trợ luôn nhé, quá xịn 🎉🎊

Sử dụng next/image như thế nào ? 🤔

Trước hết, để sử dụng next/image chúng ta cần import nó vào đã

import Image from 'next/image'

Tiếp theo, là đặt đường dẫn ảnh vào src là xong

import Image from 'next/image'
import profilePic from '../public/me.png'

//...
      <Image
        src={profilePic}
        alt="Picture of the author"
      />
        // width={500} được nhập tự động
        // height={500} được nhập tự động
        // blurDataURL="data:..." được nhập tự động
//...

Sử dụng các thuộc tính layout, placeholder, priority đúng đắn 📚 🤓

Độ ưu tiên priority 🗻

Những ảnh có thuộc tính priority sẽ có độ ưu tiên cao và được xem xét tải trước để cải thiện LCP

Chỉ nên sử dụng thuộc tính này khi ảnh nằm trong nửa trên của trang đẩu tiên

Cài đặt priority ở  các khung màu đỏ
Cài đặt priority ở các khung màu đỏ (Xem ảnh gốc)

Sử dụng layout=intrinsic

Là kiểu layout mặc định, nên bạn không cần phải truyền giá trị intrinsic. Ở chế độ này widthheight tự động fit với container chứa ảnh

<Image id="img" src={url} />

Chế độ này không thể tự động resize ảnh về các kích thước nhỏ hơn để phù hợp với từng thiết bị có viewport khác nhau nên với những bức ảnh có độ phân giải lớn thì việc sử dụng thuộc tính này sẽ không đem lại lợi ích về mặt hiệu suất 1 cách triêt để

next-image-6.png
next-image-6.png (Xem ảnh gốc)

Tất nhiên, là chế độ này vẫn hỗ trợ việc sử dụng định dạng ảnh .webp nên dung lượng của ảnh được tối ưu rất tốt

Sử dụng layout=fixed

Gần như tương tự với intrinsic, ngoại trừ việc phải cung cấp widthheight cho ảnh. Chế độ này không thể tự động responsive theo sự thay đổi kích thước của thiết bị 😕

Sử dụng layout=responsive

Một số yêu cầu bắt buộc ở chế độ này như sau

  • 👊 Phải cung cấp thuộc tính widthheight
  • 👊 Phải cung cấp giá trị layout=responsive

Bên cạnh những yêu cầu bắt buộc thì nó cũng đem lại nhiều cải thiện hơn những chế độ tôi đã đề cập ở trên

  • ✔️ Tự động convert sang định dạng .webp
  • ✔️ Tự động resize hình ảnh theo các kích thước khác nhau của device sử dụng thuộc tính sizes

Cách sử dụng thì khá đơn giản như sau

<Image
  id="img"
  src={ "/test-image/unnamed.jpg?inline=false" }
  width="1200"
  height="1200"
  layout="responsive"
/>

Với những cài đặt đơn giản trên, next/image sẽ dựa vào kích thước của ảnh và thiết bị để tạo ra 1 loạt những cài đặt cho srcset, sizes, ...

// next.config.js - đây là cài đặt mặc định của next/image
module.exports = {
  images: {
    deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
  },
}

Kết quả của việc cài đặt này như sau

Tự động tạo ảnh với nhiều kích thước khác nhau
Tự động tạo ảnh với nhiều kích thước khác nhau (Xem ảnh gốc)

Chúng ta sẽ bắt đầu test với kích thước màn hình nhỏ, sau khi được kéo dãn ra rộng bằng màn hình thì ảnh với các kích thước khác nhau sẽ được tải xuống và sử dụng, các bạn có thể kiểm tra bằng cách kiểm tra bên dưới

console.log(document.getElementById('image-id').currentSrc)

Điều này có nghĩa là, nếu bạn resize đến kích thước <= 640px thì chỉ ảnh tương ứng với kích thước này được hiện thị

Sử dụng layout=responsivesizes

Một lưu ý đặc biệt cần nắm được khi sử dụng chế độ layout này đó là nên sử dụng cùng với thuộc tính sizes

Cùng xem xét ví dụ sau: đặt 1 bức ảnh trong 1 container có độ rộng là 100px. Kết quả rất dễ hình dung, ảnh sẽ được đặt vừa với container

<div style={{ width: 100 }}>
    <Image
      id="img"
      src={ "/test-image/unnamed.jpg?inline=false" }
      width="1200"
      height="1200"
      layout="responsive"
    />
</div>

Vấn đề ở đây là, nếu bức hình có độ rộng là 1920px thì nó vẫn sẽ được tải xuống với kích thước này. Ảnh hưởng đến hiệu suất khi tải trang

Ảnh hiển thị trong khung nhỏ nhưng vẫn sử dụng kích thước lớn
Ảnh hiển thị trong khung nhỏ nhưng vẫn sử dụng kích thước lớn (Xem ảnh gốc)

Để giải quyết vấn đề này, chúng ra sẽ sử dụng thuộc tính sizes. Chỉ tải ảnh có kích thước 384px ở tất cả các khung hình

<div style={{ width: 100 }}>
    <Image
      id="img"
      src={ "/test-image/unnamed.jpg?inline=false" }
      width="1200"
      height="1200"
      layout="responsive"
      sizes="(max-width: 640px) 384px,
      (max-width: 1080px) 384px,
      (max-width: 1200px) 384px,
      (max-width: 1920px) 384px,
      384px"
    />
</div>

Có thể hiểu đoạn code trên như sau: (tài liệu về thuộc tính sizes)

  • nếu <= 640px sử dụng ảnh 384px
  • nếu <= 1080px sử dụng ảnh 384px
  • cứ như thế, cho đến 1920px

Sử dụng cách này có thể làm tăng tối đa hiệu suất tải trang
Sử dụng cách này có thể làm tăng tối đa hiệu suất tải trang (Xem ảnh gốc)

Sử dụng layout=fill

Chế độ này chỉ khác với reposive ở việc hiển thị hình ảnh. Mặc định, khi sử dụng thì widthheight sẽ được kéo dài cho vừa với container chứa ảnh

        <Image
          id="img"
          src={
            "/test-image/unnamed.jpg?inline=false"
          }
          width="50"
          height="50"
          alt="Picture of the author"
          layout="fill"
          objectFit="contain"
        />

Do đó, gây ra hiện tượng ảnh bị dãn, bị méo. Nên cần phải sử dụng kèm với thuocj tính objectFit

Sử dụng placeholder

Cách sử dụng khá đơn giản với ảnh local, mục đích là tạo lớp mờ hiển thị trước khi ảnh được load hoàn chỉnh

import mountains from '../public/mountains.jpg'

// ...
<Image
  alt="Mountains"
  src={mountains}
  placeholder="blur"
/>

Với ảnh remote thì bạn sẽ cần sử dụng blurDataURL (tham khảo)

const keyStr =
  'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='

const triplet = (e1, e2, e3) =>
  keyStr.charAt(e1 >> 2) +
  keyStr.charAt(((e1 & 3) << 4) | (e2 >> 4)) +
  keyStr.charAt(((e2 & 15) << 2) | (e3 >> 6)) +
  keyStr.charAt(e3 & 63)

const rgbDataURL = (r, g, b) =>
  `data:image/gif;base64,R0lGODlhAQABAPAA${
    triplet(0, r, g) + triplet(b, 255, 255)
  }/yH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==`

const Color = () => (
  <div>
    <Image
      alt="Cat"
      src="/cat.jpg"
      placeholder="blur"
      blurDataURL={rgbDataURL(2, 129, 210)} // <==
      width={750}
      height={1000}
    />
  </div>
)

Sử dụng loader

Nếu bạn vẫn đang thắc mắc rằng next/image tạo ra ảnh .webp và thay đổi chất lượng ảnh ở đâu? hay resize ảnh chỗ nào ? Câu trả lời là những công việc đó được thực hiện bởi loader

Mặc định, Next.js cung cấp loader để hỗ trợ việc tối ưu này rồi, nên bạn sẽ không cần phải cài đặt nữa

Nhưng nếu bạn đang sử dụng những provider khác, thì bạn vẫn có thể thay đổi cài đặt trong next.config.js (danh sách loader hỗ trợ)

module.exports = {
  images: {
    loader: 'imgix',
    path: 'https://example.com/myaccount/',
  },
}

Kết 🍰

Tóm lại, để sử dụng next/image 1 cách hiệu quả thì các bạn cần lưu ý đến mục đích sử dụng của ảnh, qua đó lựa chọn chế độ layout phù hợp. Hi vọng hiện tại các bạn đã có thể hiểu cách sử dụng next/image

Nguồn tham khảo
https://nextjs.org/docs/api-reference/next/image
https://nextjs.org/docs/basic-features/image-optimization