ساخت وضعیت‌های بارگذاری معنادار در Next.js – استریمینگ، پیش‌بارگذاری و دریافت موازی داده‌ها

در Next.js می‌توان با استفاده از مرزهای Suspense، دریافت موازی داده‌ها، و پیش‌بارگذاری هوشمند، وضعیت‌های بارگذاری معنادار طراحی کرد. این مقاله نحوهٔ نمایش فوری UI موقتی، جلوگیری از تأخیر در رندر، و استفاده از تکنیک‌هایی مانند Promise.all و preload را برای بهبود تجربه کاربری بررسی می‌کند.

loading stateparallel data fetchingSuspensepreload

~3 دقیقه مطالعه • بروزرسانی ۳ آبان ۱۴۰۴

مقدمه


وضعیت بارگذاری فوری (Instant Loading State) رابطی موقتی است که بلافاصله پس از ناوبری به کاربر نمایش داده می‌شود. برای تجربهٔ کاربری بهتر، توصیه می‌شود از اسکلت‌ها، اسپینرها یا بخش‌هایی از رابط نهایی مانند عکس کاور یا عنوان استفاده شود تا کاربر حس کند اپلیکیشن در حال پاسخ‌گویی است.


دریافت ترتیبی داده‌ها


در دریافت ترتیبی، کامپوننت‌های تو در تو هرکدام دادهٔ خود را جداگانه دریافت می‌کنند و این درخواست‌ها deduplicate نمی‌شوند. اگر کامپوننتی به خروجی کامپوننت دیگر وابسته باشد، این الگو مفید است.


مثلاً کامپوننت <Playlists> فقط پس از دریافت اطلاعات <Artist> شروع به دریافت داده می‌کند:


// app/artist/[username]/page.tsx
export default async function Page({ params }) {
  const { username } = await params
  const artist = await getArtist(username)

  return (
    <>
      <h1>{artist.name}</h1>
      <Suspense fallback={<div>Loading...</div>}>
        <Playlists artistID={artist.id} />
      </Suspense>
    </>
  )
}

async function Playlists({ artistID }) {
  const playlists = await getArtistPlaylists(artistID)
  return (
    <ul>
      {playlists.map((playlist) => (
        <li key={playlist.id}>{playlist.name}</li>
      ))}
    </ul>
  )
}

دریافت موازی داده‌ها


در دریافت موازی، درخواست‌ها هم‌زمان آغاز می‌شوند. در Next.js، صفحات و لایه‌ها به‌صورت پیش‌فرض موازی رندر می‌شوند، اما درون یک کامپوننت، درخواست‌های پشت سر هم همچنان ترتیبی هستند.


برای دریافت موازی، ابتدا درخواست‌ها را آغاز کرده و سپس با Promise.all آن‌ها را await کنید:


// app/artist/[username]/page.tsx
export default async function Page({ params }) {
  const { username } = await params

  const artistData = getArtist(username)
  const albumsData = getAlbums(username)

  const [artist, albums] = await Promise.all([artistData, albumsData])

  return (
    <>
      <h1>{artist.name}</h1>
      <Albums list={albums} />
    </>
  )
}

نکته: اگر یکی از درخواست‌ها در Promise.all شکست بخورد، کل عملیات شکست می‌خورد. برای کنترل بهتر، از Promise.allSettled استفاده کنید.


پیش‌بارگذاری داده‌ها


می‌توان داده‌ها را قبل از بلاک شدن مسیر، با تابع preload() آغاز کرد. این کار باعث می‌شود هنگام رندر کامپوننت، داده‌ها از قبل آماده باشند:


// app/item/[id]/page.tsx
export default async function Page({ params }) {
  const { id } = await params
  preload(id)
  const isAvailable = await checkIsAvailable()
  return isAvailable ? <Item id={id} /> : null
}

export const preload = (id) => {
  void getItem(id)
}

export async function Item({ id }) {
  const result = await getItem(id)
  // ...
}

کش کردن توابع دریافت داده


برای کش کردن توابع دریافت داده، می‌توان از cache در React و پکیج server-only استفاده کرد:


// utils/get-item.ts
import { cache } from 'react'
import 'server-only'
import { getItem } from '@/lib/data'

export const preload = (id) => {
  void getItem(id)
}

export const getItem = cache(async (id) => {
  // ...
})

جمع‌بندی


با استفاده از وضعیت‌های بارگذاری معنادار، دریافت موازی داده‌ها، و پیش‌بارگذاری هوشمند، می‌توان تجربه‌ای سریع، روان و قابل اعتماد برای کاربر در Next.js فراهم کرد. استفاده از مرزهای <Suspense> و کش سروری باعث بهینه‌سازی رندر و کاهش زمان انتظار می‌شود.


نوشته و پژوهش شده توسط دکتر شاهین صیامی