التحديث التدريجي للصفحات الثابتة (ISR)

أمثلة

يسمح لك Next.js بإنشاء أو تحديث الصفحات الثابتة بعد بناء موقعك. يتيح لك التحديث التدريجي للصفحات الثابتة (ISR) استخدام التوليد الثابت على أساس كل صفحة، دون الحاجة إلى إعادة بناء الموقع بالكامل. مع ISR، يمكنك الاحتفاظ بمزايا التوليد الثابت مع التوسع إلى ملايين الصفحات.

معلومة مفيدة: وقت التشغيل edge غير متوافق حاليًا مع ISR، على الرغم من أنه يمكنك الاستفادة من stale-while-revalidate عن طريق تعيين رأس cache-control يدويًا.

لاستخدام ISR، أضف الخاصية revalidate إلى getStaticProps:

function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

// يتم استدعاء هذه الدالة أثناء البناء على جانب الخادم.
// قد يتم استدعاؤها مرة أخرى، على دالة بدون خادم، إذا
// تم تمكين إعادة التحقق وجاء طلب جديد
export async function getStaticProps() {
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  return {
    props: {
      posts,
    },
    // ستحاول Next.js إعادة توليد الصفحة:
    // - عندما يأتي طلب
    // - على الأكثر مرة كل 10 ثوانٍ
    revalidate: 10, // بالثواني
  }
}

// يتم استدعاء هذه الدالة أثناء البناء على جانب الخادم.
// قد يتم استدعاؤها مرة أخرى، على دالة بدون خادم، إذا
// لم يتم توليد المسار.
export async function getStaticPaths() {
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  // الحصول على المسارات التي نريد تقديمها مسبقًا بناءً على المنشورات
  const paths = posts.map((post) => ({
    params: { id: post.id },
  }))

  // سنقوم بتقديم هذه المسارات مسبقًا فقط أثناء البناء.
  // { fallback: 'blocking' } سيقوم بتقديم الصفحات على الخادم
  // عند الطلب إذا لم يكن المسار موجودًا.
  return { paths, fallback: 'blocking' }
}

export default Blog

عندما يتم تقديم طلب لصفحة تم تقديمها مسبقًا أثناء البناء، سيتم عرض الصفحة المخزنة مؤقتًا في البداية.

  • أي طلبات للصفحة بعد الطلب الأولي وقبل 10 ثوانٍ يتم تخزينها أيضًا وتكون فورية.
  • بعد نافذة الـ10 ثوانٍ، سيظل الطلب التالي يعرض الصفحة المخزنة (القديمة)
  • تقوم Next.js بتشغيل إعادة توليد الصفحة في الخلفية.
  • بمجرد توليد الصفحة بنجاح، ستقوم Next.js بإبطال التخزين المؤقت وعرض الصفحة المحدثة. إذا فشل التوليد في الخلفية، ستظل الصفحة القديمة دون تغيير.

عند تقديم طلب لمسار لم يتم توليده، ستقوم Next.js بتقديم الصفحة على الخادم عند الطلب الأول. ستخدم الطلبات المستقبلية الملف الثابت من التخزين المؤقت. يحافظ ISR على Vercel على التخزين المؤقت عالميًا ويتعامل مع التراجعات.

معلومة مفيدة: تحقق مما إذا كان مزود البيانات العلوي لديك قد مكّن التخزين المؤقت افتراضيًا. قد تحتاج إلى تعطيله (مثل useCdn: false)، وإلا لن تتمكن إعادة التحقق من سحب بيانات جديدة لتحديث تخزين ISR المؤقت. يمكن أن يحدث التخزين المؤقت في CDN (لنقطة النهاية المطلوبة) عندما يعيد رأس Cache-Control.

إعادة التحقق عند الطلب

إذا قمت بتعيين وقت revalidate بقيمة 60، فسيرى جميع الزوار نفس النسخة المولدة من موقعك لمدة دقيقة واحدة. الطريقة الوحيدة لإبطال التخزين المؤقت هي من خلال زيارة شخص ما لتلك الصفحة بعد انتهاء الدقيقة.

بدءًا من الإصدار v12.2.0، يدعم Next.js التحديث التدريجي للصفحات الثابتة عند الطلب لإبطال تخزين Next.js المؤقت لصفحة معينة يدويًا. هذا يجعل تحديث موقعك أسهل عندما:

  • يتم إنشاء محتوى أو تحديثه من نظام إدارة المحتوى الخاص بك
  • تتغير بيانات التجارة الإلكترونية (السعر، الوصف، الفئة، التقييمات، إلخ.)

داخل getStaticProps، لا تحتاج إلى تحديد revalidate لاستخدام إعادة التحقق عند الطلب. إذا تم حذف revalidate، ستستخدم Next.js القيمة الافتراضية false (لا إعادة تحقق) وسيتم إعادة التحقق من الصفحة عند الطلب فقط عند استدعاء revalidate().

معلومة مفيدة: لن يتم تنفيذ البرمجيات الوسيطة لطلبات ISR عند الطلب. بدلاً من ذلك، استدعِ revalidate() على المسار الدقيق الذي تريد إعادة التحقق منه. على سبيل المثال، إذا كان لديك pages/blog/[slug].js وإعادة توجيه من /post-1 -> /blog/post-1، فستحتاج إلى استدعاء res.revalidate('/blog/post-1').

استخدام إعادة التحقق عند الطلب

أولاً، قم بإنشاء رمز سري يعرفه تطبيق Next.js الخاص بك فقط. سيتم استخدام هذا السر لمنع الوصول غير المصرح به إلى مسار API إعادة التحقق. يمكنك الوصول إلى المسار (إما يدويًا أو باستخدام webhook) باستخدام هيكل URL التالي:

Terminal
https://<your-site.com>/api/revalidate?secret=<token>

بعد ذلك، أضف السر كـ متغير بيئة إلى تطبيقك. أخيرًا، قم بإنشاء مسار API إعادة التحقق:

pages/api/revalidate.js
export default async function handler(req, res) {
  // التحقق من السر لتأكيد أن هذا طلب صالح
  if (req.query.secret !== process.env.MY_SECRET_TOKEN) {
    return res.status(401).json({ message: 'رمز غير صالح' })
  }

  try {
    // يجب أن يكون هذا المسار الفعلي وليس مسارًا معاد توجيهه
    // على سبيل المثال، لـ "/blog/[slug]" يجب أن يكون "/blog/post-1"
    await res.revalidate('/path-to-revalidate')
    return res.json({ revalidated: true })
  } catch (err) {
    // إذا كان هناك خطأ، ستستمر Next.js
    // في عرض آخر صفحة تم توليدها بنجاح
    return res.status(500).send('خطأ في إعادة التحقق')
  }
}

شاهد العرض التوضيحي لدينا لرؤية إعادة التحقق عند الطلب في العمل وتقديم الملاحظات.

اختبار ISR عند الطلب أثناء التطوير

عند التشغيل محليًا باستخدام next dev، يتم استدعاء getStaticProps عند كل طلب. للتحقق من أن تكوين ISR عند الطلب صحيح، ستحتاج إلى إنشاء بناء إنتاج وتشغيل خادم الإنتاج:

Terminal
$ next build
$ next start

ثم يمكنك تأكيد أن الصفحات الثابتة قد تم إعادة التحقق منها بنجاح.

معالجة الأخطاء وإعادة التحقق

إذا حدث خطأ داخل getStaticProps عند معالجة التوليد في الخلفية، أو إذا قمت برمي خطأ يدويًا، فستستمر الصفحة الأخيرة التي تم توليدها بنجاح في الظهور. في الطلب التالي، ستقوم Next.js بإعادة محاولة استدعاء getStaticProps.

export async function getStaticProps() {
  // إذا ألقى هذا الطلب خطأ غير معالج، فلن تقوم Next.js
  // بإبطال الصفحة المعروضة حاليًا و
  // إعادة محاولة getStaticProps في الطلب التالي.
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  if (!res.ok) {
    // إذا كان هناك خطأ في الخادم، قد ترغب في
    // إلقاء خطأ بدلاً من الإرجاع حتى لا يتم تحديث التخزين المؤقت
    // حتى الطلب الناجح التالي.
    throw new Error(`فشل في جلب المنشورات، تم استلام الحالة ${res.status}`)
  }

  // إذا كان الطلب ناجحًا، قم بإرجاع المنشورات
  // وإعادة التحقق كل 10 ثوانٍ.
  return {
    props: {
      posts,
    },
    revalidate: 10,
  }
}

استضافة ISR ذاتيًا

يعمل التحديث التدريجي للصفحات الثابتة (ISR) على مواقع Next.js المضافة ذاتيًا مباشرة عند استخدام next start.

يمكنك استخدام هذا النهج عند النشر إلى منسقي الحاويات مثل Kubernetes أو HashiCorp Nomad. افتراضيًا، سيتم تخزين الأصول المولدة في الذاكرة على كل pod. هذا يعني أن كل pod سيكون لديه نسخته الخاصة من الملفات الثابتة. قد تظهر بيانات قديمة حتى يتم الوصول إلى ذلك الـ pod المحدد بطلب.

لضمان الاتساق عبر جميع الـ pods، يمكنك تعطيل التخزين المؤقت في الذاكرة. سيخبر هذا خادم Next.js بالاعتماد فقط على الأصول المولدة بواسطة ISR في نظام الملفات.

يمكنك استخدام نقطة تركيب شبكة مشتركة في Kubernetes pods (أو إعداد مشابه) لإعادة استخدام نفس التخزين المؤقت لنظام الملفات بين الحاويات المختلفة. من خلال مشاركة نفس نقطة التركيب، سيتم أيضًا مشاركة مجلد .next الذي يحتوي على تخزين next/image المؤقت وإعادة استخدامه.

لتعطيل التخزين المؤقت في الذاكرة، عيّن isrMemoryCacheSize إلى 0 في ملف next.config.js الخاص بك:

next.config.js
module.exports = {
  experimental: {
    // القيمة الافتراضية 50MB
    isrMemoryCacheSize: 0, // حجم التخزين المؤقت بالبايت
  },
}

معلومة مفيدة: قد تحتاج إلى النظر في حالة تنافس بين عدة pods تحاول تحديث التخزين المؤقت في نفس الوقت، اعتمادًا على كيفية تكوين نقطة التركيب المشتركة.

سجل الإصدارات

الإصدارالتغييرات
v12.2.0أصبح ISR عند الطلب مستقرًا
v12.1.0تمت إضافة ISR عند الطلب (بيتا).
v12.0.0تمت إضافة التراجع المعتمد على الروبوتات لـ ISR.
v9.5.0تمت إضافة المسار الأساسي.