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" من الصفحة عند الطلب الأول لمثل هذا المسار. لن يتم تقديم 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 على معرّف المقالة `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 . |