با وجود تمام بهینهسازیهای خودکار Next.js، گاهی اوقات جابهجایی بین مسیرها همچنان کند به نظر میرسد یا کاربر احساس میکند سایت واکنشی نشان نمیدهد. در ادامه، ۴ دلیل اصلی افت سرعت در ناوبری و ترفندهای مهندسی برای حل هر کدام را بررسی میکنیم.
loading.tsxوقتی کاربر روی لینکی کلیک میکند که به یک صفحه پویا (دارای دیتای زنده از دیتابیس) اشاره دارد، مرورگر باید منتظر بماند تا سرور کل صفحه را رندر کند و پاسخ را بفرستد. نبود بازخورد بصری در این فاصله، حس کندی شدیدی به کاربر میدهد.
loading.tsxکافی است یک فایل loading.tsx در پوشه مسیر داینامیک خود بسازید. این کار قابلیت پیشخوانی جزئی (Partial Prefetching) را فعال میکند؛ ساختار کلی صفحه (Layout) فورا جابهجا شده و یک اسکلت لودینگ به کاربر نشان داده میشود:
// app/blog/[slug]/loading.tsx
export default function Loading() {
return <LoadingSkeleton />
}
generateStaticParams در صفحات پویااگر صفحهای دارید که ساختار پویایی دارد (مثل /blog/[slug]) اما دیتای آن عملاً تغییرات لحظهای ندارد، Next.js به طور پیشفرض در هر درخواست کاربر آن را روی سرور رندر میکند (Dynamic Rendering)، که این کار زمانبر است.
با اضافه کردن تابع generateStaticParams به فایل صفحه، به Next.js میگویید که تمام آدرسهای ممکن را در زمان بیلد (Build Time) به صورت صفحات استاتیک و آماده خروجی بگیرد (Prerendering). حالا باز شدن این صفحات آنی خواهد بود:
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
const posts = await fetch('https://api.example.com/posts').then((res) => res.json())
// تولید آرایهای از تمام اسلاگهای موجود
return posts.map((post) => ({
slug: post.slug,
}))
}
export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params
// ...
}
در شبکههای ضعیف، عملیات پیشخوانی (Prefetching) در پسزمینه به موقع تمام نمیشود و کاربر قبل از لود شدن کامل دیتای مقصد، روی لینک کلیک میکند. در این حالت حتی فایل loading.tsx هم با تاخیر باز میشود چون هنوز دانلود نشده است.
useLinkStatusبرای اینکه کاربر فوراً متوجه شود که دستور کلیک او دریافت شده و سیستم در حال پردازش است، میتوانید به کمک هوک useLinkStatus یک لودینگ یا پروگرسبار کلاینتی مستقیماً روی خود لینک یا بالای صفحه نشان دهید:
// app/ui/loading-indicator.tsx
'use client'
import { useLinkStatus } from 'next/link'
export default function LoadingIndicator() {
const { pending } = useLinkStatus() // اگر ناوبری در جریان باشد، true میشود
return (
<span aria-hidden className={`link-hint ${pending ? 'is-pending' : ''}`} />
)
}
نکته حرفهای: با اعمال یک تاخیر انیمیشنی کوتاه (مثلاً ۱۰۰ میلیثانیه) در CSS، کاری کنید که این لودینگ فقط زمانی ظاهر شود که جابهجایی بیشتر از حد معمول طول میکشد.
گاهی اوقات برای بهینهسازی مصرف منابع سرور (مثلاً در جدولهایی با اسکرول بینهایت که صدها لینک دارند)، پروپ prefetch را روی حالت false میگذارید تا سرور زیر فشار دانلود پسزمینه نرود:
<Link prefetch={false} href="/blog">وبلاگ</Link>
اما این کار یک شمشیر دو لبه است؛ حالا صفحات استاتیک تازه بعد از کلیک دانلود میشوند و صفحات پویا باید ابتدا روی سرور رندر شوند که جابهجایی را به شدت کند میکند.
به جای خاموش کردن کامل این موتور قدرتمند، کامپوننتی بسازید که پیشخوانی را فقط زمانی فعال کند که کاربر موس خود را روی لینک میبرد (onMouseEnter). اینگونه فقط صفحاتی که احتمال ورود به آنها بالاست پیشخوانی میشوند:
// app/ui/hover-prefetch-link.tsx
'use client'
import Link from 'next/link'
import { useState } from 'react'
export default function HoverPrefetchLink({ href, children }: { href: string, children: React.ReactNode }) {
const [active, setActive] = useState(false)
return (
<Link
href={href}
prefetch={active ? null : false} // به محض هوور شدن، پیشخوانی فعال میشود
onMouseEnter={() => setActive(true)}
>
{children}
</Link>
)
}
تگ <Link> یک کامپوننت سمت کلاینت است. تا زمانی که جاوااسکریپتِ صفحه اول کاملاً دانلود و در مرورگر جفتوجور (Hydrate) نشود، این تگ نمیتواند عملیات پیشخوانی صفحات بعدی را استارت بزند. اگر حجم کدهای جاوااسکریپت اولیه شما (Bundle Size) زیاد باشد، این فاز به تاخیر میافتد.
استفاده از پکیج @next/bundle-analyzer برای پیدا کردن و حذف کتابخانههای سنگین و بدون استفاده.
انتقال حداکثری منطق و محاسبات برنامه از کامپوننتهای کلاینتی (use client) به کامپوننتهای سروری (Server Components).
| علت کندی انتقال | راهکار حل مشکل | نتیجه |
| صفحات پویای بدون لودینگ | ساخت فایل loading.tsx | فعال شدن تفکیک آنی ظاهر و دیتا |
| عدم تولید استاتیک مسیرها | استفاده از generateStaticParams | رندر شدن آنی صفحات داینامیک در فاز بیلد |
| ضعف شبکه و عدم لود پیشخوانی | استفاده از هوک useLinkStatus | ارائه فیدبک و لودینگ کلاینتی فوری به کاربر |
| خاموش بودن کلاستر Prefetch | پیادهسازی پیشخوانی در وضعیت Hover | بهینهسازی منابع سرور بدون فدا کردن سرعت |
| باندل سنگین و تاخیر هیدراسیون | حذف وابستگیها با Bundle Analyzer | فعالسازی سریعتر کدهای تعاملی کلاینت |
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: https://nextjs.org/docs/app