كيفية تنفيذ التحديث الثابت التدريجي (ISR)

أمثلة

يتيح لك التحديث الثابت التدريجي (ISR) ما يلي:

  • تحديث المحتوى الثابت دون إعادة بناء الموقع بالكامل
  • تقليل حمل الخادم عن طريق تقديم صفحات ثابتة مسبقة التصيير لمعظم الطلبات
  • ضمان إضافة رؤوس cache-control المناسبة تلقائيًا للصفحات
  • التعامل مع أعداد كبيرة من صفحات المحتوى دون أوقات بناء طويلة لـ next build

إليك مثالًا بسيطًا:

interface Post {
  id: string
  title: string
  content: string
}

// سيقوم Next.js بإبطال ذاكرة التخزين المؤقت عندما
// يصل طلب، على الأكثر مرة كل 60 ثانية.
export const revalidate = 60

// سنقوم بمسبق تصيير المعلمات من `generateStaticParams` فقط أثناء وقت البناء.
// إذا وصل طلب لمسار لم يتم إنشاؤه،
// سيقوم Next.js بتصيير الصفحة على الخادم عند الطلب.
export const dynamicParams = true // أو false، لإظهار خطأ 404 للمسارات غير المعروفة

export async function generateStaticParams() {
  const posts: Post[] = await fetch('https://api.vercel.app/blog').then((res) =>
    res.json()
  )
  return posts.map((post) => ({
    id: String(post.id),
  }))
}

export default async function Page({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = await params
  const post: Post = await fetch(`https://api.vercel.app/blog/${id}`).then(
    (res) => res.json()
  )
  return (
    <main>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </main>
  )
}
// سيقوم Next.js بإبطال ذاكرة التخزين المؤقت عندما
// يصل طلب، على الأكثر مرة كل 60 ثانية.
export const revalidate = 60

// سنقوم بمسبق تصيير المعلمات من `generateStaticParams` فقط أثناء وقت البناء.
// إذا وصل طلب لمسار لم يتم إنشاؤه،
// سيقوم Next.js بتصيير الصفحة على الخادم عند الطلب.
export const dynamicParams = true // أو false، لإظهار خطأ 404 للمسارات غير المعروفة

export async function generateStaticParams() {
  const posts = await fetch('https://api.vercel.app/blog').then((res) =>
    res.json()
  )
  return posts.map((post) => ({
    id: String(post.id),
  }))
}

export default async function Page({ params }) {
  const { id } = await params
  const post = await fetch(`https://api.vercel.app/blog/${id}`).then((res) =>
    res.json()
  )
  return (
    <main>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </main>
  )
}

إليك كيفية عمل هذا المثال:

  1. أثناء next build، يتم إنشاء جميع منشورات المدونة المعروفة (هناك 25 في هذا المثال)
  2. جميع الطلبات الموجهة إلى هذه الصفحات (مثل /blog/1) مخزنة مؤقتًا وفورية
  3. بعد مرور 60 ثانية، سيظل الطلب التالي يعرض الصفحة المخزنة (القديمة)
  4. يتم إبطال ذاكرة التخزين المؤقت ويبدأ إنشاء نسخة جديدة من الصفحة في الخلفية
  5. بمجرد الإنشاء بنجاح، سيعرض Next.js الصفحة المحدثة ويخزنها مؤقتًا
  6. إذا تم طلب /blog/26، سيقوم Next.js بإنشاء هذه الصفحة وتخزينها مؤقتًا عند الطلب

مرجع

تكوين جزء المسار

الدوال

أمثلة

إعادة التحقق بناءً على الوقت

هذا يسترد ويعرض قائمة بمنشورات المدونة على /blog. بعد ساعة، يتم إبطال ذاكرة التخزين المؤقت لهذه الصفحة عند الزيارة التالية للصفحة. ثم، في الخلفية، يتم إنشاء نسخة جديدة من الصفحة بأحدث منشورات المدونة.

interface Post {
  id: string
  title: string
  content: string
}

export const revalidate = 3600 // إبطال كل ساعة

export default async function Page() {
  const data = await fetch('https://api.vercel.app/blog')
  const posts: Post[] = await data.json()
  return (
    <main>
      <h1>منشورات المدونة</h1>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </main>
  )
}
export const revalidate = 3600 // إبطال كل ساعة

export default async function Page() {
  const data = await fetch('https://api.vercel.app/blog')
  const posts = await data.json()
  return (
    <main>
      <h1>منشورات المدونة</h1>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </main>
  )
}

نوصي بتعيين وقت إعادة تحقق طويل. على سبيل المثال، ساعة بدلاً من ثانية. إذا كنت بحاجة إلى دقة أكبر، ففكر في استخدام إعادة التحقق عند الطلب. إذا كنت بحاجة إلى بيانات في الوقت الفعلي، ففكر في التبديل إلى التصيير الديناميكي.

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

للحصول على طريقة أكثر دقة لإعادة التحقق، يمكنك إبطال الصفحات عند الطلب باستخدام دالة revalidatePath.

على سبيل المثال، سيتم استدعاء هذا الإجراء الخادم بعد إضافة منشور جديد. بغض النظر عن كيفية استرداد البيانات في مكون الخادم الخاص بك، سواء باستخدام fetch أو الاتصال بقاعدة بيانات، سيؤدي هذا إلى مسح ذاكرة التخزين المؤقت للمسار بالكامل والسماح لمكون الخادم باسترداد بيانات جديدة.

'use server'

import { revalidatePath } from 'next/cache'

export async function createPost() {
  // إبطال مسار /posts في ذاكرة التخزين المؤقت
  revalidatePath('/posts')
}
'use server'

import { revalidatePath } from 'next/cache'

export async function createPost() {
  // إبطال مسار /posts في ذاكرة التخزين المؤقت
  revalidatePath('/posts')
}

عرض تجريبي واستكشف الكود المصدري.

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

لمعظم حالات الاستخدام، يُفضل إبطال المسارات بالكامل. إذا كنت بحاجة إلى تحكم أكثر دقة، يمكنك استخدام دالة revalidateTag. على سبيل المثال، يمكنك وضع علامة على استدعاءات fetch الفردية:

export default async function Page() {
  const data = await fetch('https://api.vercel.app/blog', {
    next: { tags: ['posts'] },
  })
  const posts = await data.json()
  // ...
}
export default async function Page() {
  const data = await fetch('https://api.vercel.app/blog', {
    next: { tags: ['posts'] },
  })
  const posts = await data.json()
  // ...
}

إذا كنت تستخدم ORM أو تتصل بقاعدة بيانات، يمكنك استخدام unstable_cache:

import { unstable_cache } from 'next/cache'
import { db, posts } from '@/lib/db'

const getCachedPosts = unstable_cache(
  async () => {
    return await db.select().from(posts)
  },
  ['posts'],
  { revalidate: 3600, tags: ['posts'] }
)

export default async function Page() {
  const posts = getCachedPosts()
  // ...
}
import { unstable_cache } from 'next/cache'
import { db, posts } from '@/lib/db'

const getCachedPosts = unstable_cache(
  async () => {
    return await db.select().from(posts)
  },
  ['posts'],
  { revalidate: 3600, tags: ['posts'] }
)

export default async function Page() {
  const posts = getCachedPosts()
  // ...
}

يمكنك بعد ذلك استخدام revalidateTag في إجراءات الخادم أو معالج المسار:

'use server'

import { revalidateTag } from 'next/cache'

export async function createPost() {
  // إبطال جميع البيانات الموسومة بـ 'posts' في ذاكرة التخزين المؤقت
  revalidateTag('posts')
}
'use server'

import { revalidateTag } from 'next/cache'

export async function createPost() {
  // إبطال جميع البيانات الموسومة بـ 'posts' في ذاكرة التخزين المؤقت
  revalidateTag('posts')
}

التعامل مع الاستثناءات غير الملتقطة

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

تخصيص موقع ذاكرة التخزين المؤقت

يمكنك تكوين موقع ذاكرة التخزين المؤقت لـ Next.js إذا كنت تريد الاحتفاظ بالصفحات والبيانات المخزنة مؤقتًا في تخزين دائم، أو مشاركة ذاكرة التخزين المؤقت عبر عدة حاويات أو نسخ من تطبيق Next.js الخاص بك. تعلم المزيد.

استكشاف الأخطاء وإصلاحها

تصحيح البيانات المخزنة مؤقتًا في التطوير المحلي

إذا كنت تستخدم واجهة برمجة التطبيقات fetch، يمكنك إضافة سجلات إضافية لفهم أي الطلبات مخزنة مؤقتًا أو غير مخزنة. تعلم المزيد عن خيار logging.

next.config.js
module.exports = {
  logging: {
    fetches: {
      fullUrl: true,
    },
  },
}

التحقق من سلوك الإنتاج الصحيح

للتحقق من أن صفحاتك مخزنة مؤقتًا ويتم إعادة التحقق منها بشكل صحيح في بيئة الإنتاج، يمكنك الاختبار محليًا عن طريق تشغيل next build ثم next start لتشغيل خادم Next.js للإنتاج.

هذا سيسمح لك باختبار سلوك التوليد التدريجي الثابت (ISR) كما يعمل في بيئة الإنتاج. لمزيد من التصحيح، أضف متغير البيئة التالي إلى ملف .env الخاص بك:

.env
NEXT_PRIVATE_DEBUG_CACHE=1

هذا سيجعل خادم Next.js يسجل في الكونسول عمليات الوصول والإخفاق في ذاكرة التخزين المؤقت لـ ISR. يمكنك فحص المخرجات لمعرفة الصفحات التي يتم توليدها أثناء next build، وكذلك كيف يتم تحديث الصفحات عند الوصول إلى المسارات عند الطلب.

محاذير

  • التوليد التدريجي الثابت (ISR) مدعوم فقط عند استخدام بيئة تشغيل Node.js (الافتراضية).
  • التوليد التدريجي الثابت (ISR) غير مدعوم عند إنشاء تصدير ثابت.
  • إذا كان لديك عدة طلبات fetch في مسار تم عرضه بشكل ثابت، ولكل منها تردد revalidate مختلف، سيتم استخدام أقل وقت لـ ISR. ومع ذلك، سيتم احترام ترددات revalidate هذه بواسطة ذاكرة التخزين المؤقت للبيانات.
  • إذا كان لأي من طلبات fetch المستخدمة في مسار وقت revalidate بقيمة 0، أو no-store صريح، سيتم عرض المسار ديناميكيًا.
  • لن يتم تنفيذ Middleware لطلبات ISR عند الطلب، مما يعني أن أي إعادة كتابة للمسار أو منطق في Middleware لن يتم تطبيقه. تأكد من أنك تقوم بإعادة التحقق من المسار الدقيق. على سبيل المثال، /post/1 بدلاً من /post-1 المعاد كتابتها.

دعم المنصات

خيار النشرمدعوم
خادم Node.jsنعم
حاوية Dockerنعم
تصدير ثابتلا
المحولاتحسب المنصة

تعلم كيفية تكوين ISR عند استضافة Next.js ذاتيًا.

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

الإصدارالتغييرات
v14.1.0cacheHandler المخصص أصبح مستقرًا.
v13.0.0تم تقديم App Router.
v12.2.0Pages Router: أصبح التوليد التدريجي الثابت عند الطلب (On-Demand ISR) مستقرًا
v12.0.0Pages Router: تمت إضافة الاسترجاع التلقائي لـ ISR مع مراعاة الروبوتات.
v9.5.0Pages Router: تم تقديم ISR المستقر.