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 يحتوي على `id` للمقالة.
// إذا كان المسار مثل /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) من الصفحة عند الطلب الأول لمثل هذا المسار. لن يتم تقديم نسخة احتياطية لمحركات البحث مثل Google، وبدلاً من ذلك سيتصرف المسار كما في
fallback: 'blocking'
. - عند التنقل إلى صفحة بها
fallback: true
عبرnext/link
أوnext/router
(من جانب العميل)، لن يقدم Next.js نسخة احتياطية وبدلاً من ذلك ستصرف الصفحة كما فيfallback: 'blocking'
. - في الخلفية، سيقوم Next.js بإنشاء
HTML
وJSON
للمسار المطلوب بشكل ثابت. وهذا يشمل تشغيلgetStaticProps
. - عند الانتهاء، يتلقى المتصفح
JSON
للمسار الذي تم إنشاؤه. سيتم استخدام هذا لعرض الصفحة تلقائيًا مع الخصائص المطلوبة. من وجهة نظر المستخدم، ستنتقل الصفحة من الصفحة الاحتياطية إلى الصفحة الكاملة. - في نفس الوقت، يضيف 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
للمسار الذي تم إنشاؤه. من وجهة نظر المستخدم، سينتقل من "المتصفح يطلب الصفحة" إلى "تم تحميل الصفحة بالكامل". لا يوجد وميض لحالة التحميل/الاحتياطي. - في نفس الوقت، يضيف Next.js هذا المسار إلى قائمة الصفحات المحضرة مسبقًا. الطلبات اللاحقة لنفس المسار ستقدم الصفحة التي تم إنشاؤها، مثل الصفحات الأخرى المحضرة مسبقًا أثناء وقت البناء.
fallback: 'blocking'
لن يحدث الصفحات التي تم إنشاؤها افتراضيًا. لتحديث الصفحات التي تم إنشاؤها، استخدم التجديد الثابت التدريجي (Incremental Static Regeneration) بالتزامن مع fallback: 'blocking'
.
ملاحظة جيدة: لا يتم دعم
fallback: 'blocking'
عند استخدامoutput: 'export'
.
الصفحات الاحتياطية (Fallback pages)
في النسخة "الاحتياطية" من الصفحة:
- ستكون خصائص الصفحة فارغة.
- باستخدام الموجه (router)، يمكنك اكتشاف ما إذا كان يتم عرض النسخة الاحتياطية،
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 يحتوي على `id` للمقالة.
// إذا كان المسار مثل /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 . |