getStaticPaths
عند تصدير دالة تسمى getStaticPaths
من صفحة تستخدم المسارات الديناميكية (Dynamic Routes)، فإن Next.js سيقوم بتحضير جميع المسارات المحددة بواسطة getStaticPaths
بشكل ثابت مسبقًا.
import type {
InferGetStaticPropsType,
GetStaticProps,
GetStaticPaths,
} from 'next'
type Repo = {
name: string
stargazers_count: number
}
export const getStaticPaths = (async () => {
return {
paths: [
{
params: {
name: 'next.js',
},
}, // انظر قسم "paths" أدناه
],
fallback: true, // false أو "blocking"
}
}) satisfies GetStaticPaths
export const getStaticProps = (async (context) => {
const res = await fetch('https://api.github.com/repos/vercel/next.js')
const repo = await res.json()
return { props: { repo } }
}) satisfies GetStaticProps<{
repo: Repo
}>
export default function Page({
repo,
}: InferGetStaticPropsType<typeof getStaticProps>) {
return repo.stargazers_count
}
export async function getStaticPaths() {
return {
paths: [
{
params: {
name: 'next.js',
},
}, // انظر قسم "paths" أدناه
],
fallback: true, // false أو "blocking"
}
}
export async function getStaticProps() {
const res = await fetch('https://api.github.com/repos/vercel/next.js')
const repo = await res.json()
return { props: { repo } }
}
export default function Page({ repo }) {
return repo.stargazers_count
}
قيم إرجاع getStaticPaths
يجب أن ترجع دالة getStaticPaths
كائنًا يحتوي على الخصائص المطلوبة التالية:
paths
يحدد مفتاح paths
المسارات التي سيتم تحضيرها مسبقًا. على سبيل المثال، لنفترض أن لديك صفحة تستخدم مسارات ديناميكية (Dynamic Routes) باسم pages/posts/[id].js
. إذا قمت بتصدير getStaticPaths
من هذه الصفحة وإرجاع ما يلي لـ paths
:
return {
paths: [
{ params: { id: '1' }},
{
params: { id: '2' },
// مع تكوين i18n يمكن إرجاع اللغة للمسار أيضًا
locale: "en",
},
],
fallback: ...
}
عندها، سيقوم Next.js بإنشاء /posts/1
و /posts/2
بشكل ثابت أثناء next build
باستخدام مكون الصفحة في pages/posts/[id].js
.
يجب أن تتطابق قيمة كل كائن params
مع المعلمات المستخدمة في اسم الصفحة:
- إذا كان اسم الصفحة هو
pages/posts/[postId]/[commentId]
، فيجب أن يحتويparams
علىpostId
وcommentId
. - إذا كان اسم الصفحة يستخدم مسارات التقاط الكل (catch-all routes) مثل
pages/[...slug]
، فيجب أن يحتويparams
علىslug
(وهو مصفوفة). إذا كانت هذه المصفوفة['hello', 'world']
، فسيقوم Next.js بإنشاء الصفحة بشكل ثابت في/hello/world
. - إذا كانت الصفحة تستخدم مسار التقاط اختياري (optional catch-all route)، استخدم
null
أو[]
أوundefined
أوfalse
لعرض المسار الجذري. على سبيل المثال، إذا قمت بتوفيرslug: false
لـpages/[[...slug]]
، فسيقوم Next.js بإنشاء الصفحة/
بشكل ثابت.
تكون سلاسل params
حساسة لحالة الأحرف ومن الأفضل توحيدها لضمان إنشاء المسارات بشكل صحيح. على سبيل المثال، إذا تم إرجاع WoRLD
لمعلمة، فسوف تتطابق فقط إذا كان WoRLD
هو المسار الذي تمت زيارته، وليس world
أو World
.
بشكل منفصل عن كائن params
، يمكن إرجاع حقل locale
عند تكوين i18n، والذي يحدد اللغة للمسار الذي يتم إنشاؤه.
fallback: false
إذا كان fallback
هو false
، فإن أي مسارات غير مرجعة بواسطة getStaticPaths
ستؤدي إلى صفحة 404.
عند تشغيل next build
، سيتحقق Next.js مما إذا كان getStaticPaths
قد أرجَع fallback: false
، ثم سيقوم ببناء فقط المسارات التي تم إرجاعها بواسطة getStaticPaths
. هذا الخيار مفيد إذا كان لديك عدد صغير من المسارات لإنشائها، أو إذا لم تتم إضافة بيانات صفحة جديدة بشكل متكرر. إذا وجدت أنك بحاجة إلى إضافة المزيد من المسارات، وكان لديك fallback: false
، فسوف تحتاج إلى تشغيل next build
مرة أخرى حتى يمكن إنشاء المسارات الجديدة.
المثال التالي يحضر صفحة مدونة واحدة لكل صفحة تسمى pages/posts/[id].js
. سيتم جلب قائمة منشورات المدونة من نظام إدارة المحتوى (CMS) وإرجاعها بواسطة getStaticPaths
. ثم، لكل صفحة، يتم جلب بيانات المنشور من نظام إدارة المحتوى باستخدام getStaticProps
.
function Post({ post }) {
// عرض المنشور...
}
// يتم استدعاء هذه الدالة أثناء وقت البناء
export async function getStaticPaths() {
// استدعاء نقطة نهاية API خارجية للحصول على المنشورات
const res = await fetch('https://.../posts')
const posts = await res.json()
// الحصول على المسارات التي نريد تحضيرها مسبقًا بناءً على المنشورات
const paths = posts.map((post) => ({
params: { id: post.id },
}))
// سنقوم بتحضير هذه المسارات مسبقًا فقط أثناء وقت البناء.
// { fallback: false } يعني أن المسارات الأخرى يجب أن تعرض 404.
return { paths, fallback: false }
}
// يتم استدعاء هذا أيضًا أثناء وقت البناء
export async function getStaticProps({ params }) {
// params يحتوي على معرف المنشور.
// إذا كان المسار مثل /posts/1، فإن params.id هو 1
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
// تمرير بيانات المنشور إلى الصفحة عبر props
return { props: { post } }
}
export default Post
fallback: true
إذا كان fallback
هو true
، فإن سلوك getStaticProps
يتغير بالطرق التالية:
- سيتم تحويل المسارات المرجعة من
getStaticPaths
إلىHTML
أثناء وقت البناء بواسطةgetStaticProps
. - المسارات التي لم يتم إنشاؤها أثناء وقت البناء لن تؤدي إلى صفحة 404. بدلاً من ذلك، سيقدم Next.js نسخة "fallback" من الصفحة عند الطلب الأول لهذا المسار. لن يتم تقديم fallback لمحركات البحث مثل Google، وسيتصرف المسار كما في
fallback: 'blocking'
. - عند التنقل إلى صفحة بـ
fallback: true
عبرnext/link
أوnext/router
(من جانب العميل)، لن يقدم Next.js fallback وسيتصرف الصفحة كما فيfallback: 'blocking'
. - في الخلفية، سيقوم Next.js بإنشاء
HTML
وJSON
للمسار المطلوب بشكل ثابت. وهذا يشمل تشغيلgetStaticProps
. - عند الانتهاء، يتلقى المتصفح
JSON
للمسار الذي تم إنشاؤه. سيتم استخدام هذا لعرض الصفحة تلقائيًا مع الـ props المطلوبة. من وجهة نظر المستخدم، سيتم تبديل الصفحة من صفحة fallback إلى الصفحة الكاملة. - في نفس الوقت، يضيف Next.js هذا المسار إلى قائمة الصفحات المحضرة مسبقًا. الطلبات اللاحقة لنفس المسار ستقدم الصفحة التي تم إنشاؤها، مثل الصفحات الأخرى المحضرة مسبقًا أثناء وقت البناء.
معلومة جيدة:
fallback: true
غير مدعوم عند استخدامoutput: 'export'
.
متى يكون fallback: true
مفيدًا؟
fallback: true
مفيد إذا كان تطبيقك يحتوي على عدد كبير جدًا من الصفحات الثابتة التي تعتمد على البيانات (مثل موقع تجارة إلكترونية كبير جدًا). إذا كنت تريد تحضير جميع صفحات المنتج مسبقًا، فسيستغرق البناء وقتًا طويلاً جدًا.
بدلاً من ذلك، يمكنك إنشاء مجموعة فرعية صغيرة من الصفحات بشكل ثابت واستخدام fallback: true
للباقي. عندما يطلب شخص ما صفحة لم يتم إنشاؤها بعد، سيرى المستخدم الصفحة مع مؤشر تحميل أو مكون هيكل عظمي.
بعد فترة وجيزة، ينتهي getStaticProps
وسيتم عرض الصفحة بالبيانات المطلوبة. من الآن فصاعدًا، سيحصل كل من يطلب نفس الصفحة على الصفحة المحضرة مسبقًا بشكل ثابت.
هذا يضمن أن المستخدمين يحصلون دائمًا على تجربة سريعة مع الحفاظ على عمليات بناء سريعة وفوائد التوليد الثابت.
fallback: true
لن يقوم بتحديث الصفحات التي تم إنشاؤها، لذلك يمكنك الاطلاع على التجديد الثابت التدريجي (Incremental Static Regeneration).
fallback: 'blocking'
إذا كان fallback
هو 'blocking'
، فإن المسارات الجديدة غير المرجعة بواسطة getStaticPaths
ستنتظر إنشاء HTML
، مماثل لـ SSR (ومن هنا جاءت تسمية blocking)، ثم يتم تخزينها مؤقتًا للطلبات المستقبلية بحيث يحدث هذا مرة واحدة فقط لكل مسار.
سيتصرف getStaticProps
كما يلي:
- سيتم تحويل المسارات المرجعة من
getStaticPaths
إلىHTML
أثناء وقت البناء بواسطةgetStaticProps
. - المسارات التي لم يتم إنشاؤها أثناء وقت البناء لن تؤدي إلى صفحة 404. بدلاً من ذلك، سيقوم Next.js بعرض SSR عند الطلب الأول وإرجاع
HTML
الذي تم إنشاؤه. - عند الانتهاء، يتلقى المتصفح
HTML
للمسار الذي تم إنشاؤه. من وجهة نظر المستخدم، سينتقل من "المتصفح يطلب الصفحة" إلى "تم تحميل الصفحة بالكامل". لا يوجد وميض لحالة التحميل/fallback. - في نفس الوقت، يضيف Next.js هذا المسار إلى قائمة الصفحات المحضرة مسبقًا. الطلبات اللاحقة لنفس المسار ستقدم الصفحة التي تم إنشاؤها، مثل الصفحات الأخرى المحضرة مسبقًا أثناء وقت البناء.
fallback: 'blocking'
لن يقوم بتحديث الصفحات التي تم إنشاؤها افتراضيًا. لتحديث الصفحات التي تم إنشاؤها، استخدم التجديد الثابت التدريجي (Incremental Static Regeneration) بالتزامن مع fallback: 'blocking'
.
معلومة جيدة:
fallback: 'blocking'
غير مدعوم عند استخدامoutput: 'export'
.
صفحات Fallback
في نسخة "fallback" من الصفحة:
- ستكون props الصفحة فارغة.
- باستخدام الموجه (router)، يمكنك اكتشاف ما إذا كان يتم عرض fallback،
router.isFallback
سيكونtrue
.
يوضح المثال التالي استخدام isFallback
:
import { useRouter } from 'next/router'
function Post({ post }) {
const router = useRouter()
// إذا لم يتم إنشاء الصفحة بعد، سيتم عرض هذا
// في البداية حتى ينتهي تشغيل getStaticProps()
if (router.isFallback) {
return <div>جار التحميل...</div>
}
// عرض المنشور...
}
// يتم استدعاء هذه الدالة أثناء وقت البناء
export async function getStaticPaths() {
return {
// فقط `/posts/1` و `/posts/2` يتم إنشاؤها أثناء وقت البناء
paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
// تمكين إنشاء صفحات إضافية بشكل ثابت
// على سبيل المثال: `/posts/3`
fallback: true,
}
}
// يتم استدعاء هذا أيضًا أثناء وقت البناء
export async function getStaticProps({ params }) {
// params يحتوي على معرف المنشور.
// إذا كان المسار مثل /posts/1، فإن params.id هو 1
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
// تمرير بيانات المنشور إلى الصفحة عبر props
return {
props: { post },
// إعادة إنشاء المنشور مرة واحدة كحد أقصى كل ثانية
// إذا جاء طلب
revalidate: 1,
}
}
export default Post
سجل الإصدارات
الإصدار | التغييرات |
---|---|
v13.4.0 | موجه التطبيق (App Router) أصبح الآن مستقرًا مع جلب بيانات مبسط، بما في ذلك generateStaticParams() |
v12.2.0 | التجديد الثابت التدريجي عند الطلب (On-Demand Incremental Static Regeneration) أصبح مستقرًا. |
v12.1.0 | تمت إضافة التجديد الثابت التدريجي عند الطلب (On-Demand Incremental Static Regeneration) (بيتا). |
v9.5.0 | أصبح التجديد الثابت التدريجي (Incremental Static Regeneration) مستقرًا. |
v9.3.0 | تم تقديم getStaticPaths . |