التخزين المؤقت في Next.js

يحسن Next.js أداء تطبيقك ويقلل التكاليف عن طريق تخزين أعمال التصيير وطلبات البيانات مؤقتًا. توفر هذه الصفحة نظرة متعمقة على آليات التخزين المؤقت في Next.js، وواجهات برمجة التطبيقات (APIs) التي يمكنك استخدامها لتكوينها، وكيفية تفاعلها مع بعضها البعض.

معلومة مفيدة: هذه الصفحة تساعدك على فهم كيفية عمل Next.js تحت الغطاء ولكنها ليست معرفة أساسية لتكون منتجًا باستخدام Next.js. يتم تحديد معظم استدلالات التخزين المؤقت في Next.js من خلال استخدامك لواجهة برمجة التطبيقات ولها إعدادات افتراضية لأفضل أداء مع تكوين صفري أو ضئيل.

نظرة عامة

إليك نظرة عامة عالية المستوى على آليات التخزين المؤقت المختلفة والغرض منها:

الآليةماذاأينالغرضالمدة
تخزين الطلبات مؤقتًاقيم الإرجاع للوظائفالخادمإعادة استخدام البيانات في شجرة مكونات Reactدورة حياة الطلب
ذاكرة التخزين المؤقت للبياناتالبياناتالخادمتخزين البيانات عبر طلبات المستخدم والنشراتدائمة (يمكن إعادة التحقق منها)
ذاكرة التخزين المؤقت الكاملة للطريقHTML وحمولة RSCالخادمتقليل تكلفة التصيير وتحسين الأداءدائمة (يمكن إعادة التحقق منها)
ذاكرة التخزين المؤقت للموجهحمولة RSCالعميلتقليل طلبات الخادم أثناء التنقلجلسة المستخدم أو زمنية

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

رسم بياني يوضح سلوك التخزين المؤقت الافتراضي في Next.js للآليات الأربع، مع HIT و MISS و SET في وقت البناء وعند زيارة الطريق لأول مرة.

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

تخزين الطلبات مؤقتًا (Request Memoization)

يمتد React واجهة برمجة التطبيقات fetch لتخزين الطلبات التي لها نفس URL والخيارات تلقائيًا مؤقتًا. هذا يعني أنه يمكنك استدعاء دالة fetch لنفس البيانات في أماكن متعددة في شجرة مكونات React مع تنفيذها مرة واحدة فقط.

طلبات Fetch المكررة

على سبيل المثال، إذا كنت بحاجة إلى استخدام نفس البيانات عبر طريق (مثل في Layout و Page ومكونات متعددة)، لا يتعين عليك جلب البيانات في أعلى الشجرة، وإعادة توجيه الخصائص بين المكونات. بدلاً من ذلك، يمكنك جلب البيانات في المكونات التي تحتاجها دون القلق بشأن الآثار المترتبة على الأداء لإنشاء طلبات متعددة عبر الشبكة لنفس البيانات.

async function getItem() {
  // يتم تخزين دالة `fetch` تلقائيًا مؤقتًا والنتيجة
  // مخزنة مؤقتًا
  const res = await fetch('https://.../item/1')
  return res.json()
}

// يتم استدعاء هذه الدالة مرتين، ولكن يتم تنفيذها فقط في المرة الأولى
const item = await getItem() // cache MISS

// يمكن أن يكون الاستدعاء الثاني في أي مكان في طريقك
const item = await getItem() // cache HIT
async function getItem() {
  // يتم تخزين دالة `fetch` تلقائيًا مؤقتًا والنتيجة
  // مخزنة مؤقتًا
  const res = await fetch('https://.../item/1')
  return res.json()
}

// يتم استدعاء هذه الدالة مرتين، ولكن يتم تنفيذها فقط في المرة الأولى
const item = await getItem() // cache MISS

// يمكن أن يكون الاستدعاء الثاني في أي مكان في طريقك
const item = await getItem() // cache HIT

كيف يعمل تخزين الطلبات مؤقتًا

رسم بياني يوضح كيفية عمل تخزين fetch مؤقتًا أثناء تصيير React.
  • أثناء تصيير طريق، عند استدعاء طلب معين لأول مرة، لن تكون نتيجته في الذاكرة وسيكون cache MISS.
  • لذلك، سيتم تنفيذ الدالة، وسيتم جلب البيانات من المصدر الخارجي، وسيتم تخزين النتيجة في الذاكرة.
  • استدعاءات الدالة اللاحقة للطلب في نفس تمرير التصيير ستكون cache HIT، وسيتم إرجاع البيانات من الذاكرة دون تنفيذ الدالة.
  • بمجرد اكتمال تصيير الطريق وتمرير التصيير، يتم "إعادة تعيين" الذاكرة ومسح جميع إدخالات تخزين الطلبات مؤقتًا.

معلومة مفيدة:

  • تخزين الطلبات مؤقتًا هو ميزة React وليست ميزة Next.js. تم تضمينها هنا لإظهار كيفية تفاعلها مع آليات التخزين المؤقت الأخرى.
  • التخزين المؤقت ينطبق فقط على طريقة GET في طلبات fetch.
  • التخزين المؤقت ينطبق فقط على شجرة مكونات React، وهذا يعني:
    • ينطبق على طلبات fetch في generateMetadata و generateStaticParams و Layouts و Pages ومكونات الخادم الأخرى.
    • لا ينطبق على طلبات fetch في Route Handlers لأنها ليست جزءًا من شجرة مكونات React.
  • للحالات التي لا تكون فيها fetch مناسبة (مثل بعض عملاء قواعد البيانات أو عملاء CMS أو عملاء GraphQL)، يمكنك استخدام دالة cache في React لتخزين الدوال مؤقتًا.

المدة

يستمر التخزين المؤقت طوال مدة طلب الخادم حتى تنتهي شجرة مكونات React من التصيير.

إعادة التحقق

نظرًا لأن التخزين المؤقت لا يتم مشاركته عبر طلبات الخادم وينطبق فقط أثناء التصيير، فلا حاجة لإعادة التحقق منه.

عدم المشاركة

ينطبق التخزين المؤقت فقط على طريقة GET في طلبات fetch، ولا يتم تخزين الطرق الأخرى مثل POST و DELETE مؤقتًا. هذا السلوك الافتراضي هو تحسين من React ولا نوصي بعدم المشاركة فيه.

لإدارة الطلبات الفردية، يمكنك استخدام خاصية signal من AbortController. ومع ذلك، هذا لن يؤدي إلى عدم تخزين الطلبات مؤقتًا، بل سيؤدي إلى إلغاء الطلبات الجارية.

app/example.js
const { signal } = new AbortController()
fetch(url, { signal })

ذاكرة التخزين المؤقت للبيانات (Data Cache)

يحتوي Next.js على ذاكرة تخزين مؤقت مدمجة للبيانات تحافظ على نتيجة جلب البيانات عبر طلبات الخادم الواردة والنشرات. هذا ممكن لأن Next.js يمتد واجهة برمجة التطبيقات fetch الأصلية للسماح لكل طلب على الخادم بتعيين دلالات التخزين المؤقت الخاصة به.

معلومة مفيدة: في المتصفح، يشير خيار cache لـ fetch إلى كيفية تفاعل الطلب مع ذاكرة التخزين المؤقت HTTP للمتصفح، في Next.js، يشير خيار cache إلى كيفية تفاعل طلب الخادم مع ذاكرة التخزين المؤقت للبيانات على الخادم.

بشكل افتراضي، يتم تخزين طلبات البيانات التي تستخدم fetch مؤقتًا. يمكنك استخدام خيارات cache و next.revalidate لـ fetch لتكوين سلوك التخزين المؤقت.

كيف تعمل ذاكرة التخزين المؤقت للبيانات

رسم بياني يوضح كيفية تفاعل طلبات fetch المخزنة وغير المخزنة مع ذاكرة التخزين المؤقت للبيانات. يتم تخزين الطلبات المخزنة في ذاكرة التخزين المؤقت للبيانات، وتخزين الطلبات غير المخزنة من مصدر البيانات، وعدم تخزينها في ذاكرة التخزين المؤقت للبيانات، وتخزينها مؤقتًا.
  • عند استدعاء طلب fetch لأول مرة أثناء التصيير، يتحقق Next.js من ذاكرة التخزين المؤقت للبيانات بحثًا عن استجابة مخزنة.
  • إذا تم العثور على استجابة مخزنة، يتم إرجاعها على الفور وتخزينها مؤقتًا.
  • إذا لم يتم العثور على استجابة مخزنة، يتم إجراء الطلب إلى مصدر البيانات، وتخزين النتيجة في ذاكرة التخزين المؤقت للبيانات، وتخزينها مؤقتًا.
  • بالنسبة للبيانات غير المخزنة (مثل { cache: 'no-store' })، يتم دائمًا جلب النتيجة من مصدر البيانات، وتخزينها مؤقتًا.
  • سواء كانت البيانات مخزنة أو غير مخزنة، يتم دائمًا تخزين الطلبات مؤقتًا لتجنب إنشاء طلبات مكررة لنفس البيانات أثناء تمرير تصيير React.

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

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

مع التخزين المؤقت، نقلل عدد الطلبات المكررة في نفس تمرير التصيير التي يجب أن تعبر حدود الشبكة من خادم التصيير إلى خادم ذاكرة التخزين المؤقت للبيانات (مثل CDN أو Edge Network) أو مصدر البيانات (مثل قاعدة بيانات أو CMS). مع ذاكرة التخزين المؤقت للبيانات، نقلل عدد الطلبات الموجهة إلى مصدر البيانات الأصلي.

المدة

ذاكرة التخزين المؤقت للبيانات تدوم عبر الطلبات الواردة والنشرات ما لم تقم بإعادة التحقق أو عدم المشاركة.

إعادة التحقق

يمكن إعادة التحقق من البيانات المخزنة مؤقتًا بطريقتين، باستخدام:

  • إعادة التحقق الزمنية: إعادة التحقق من البيانات بعد مرور فترة زمنية معينة وإجراء طلب جديد. هذا مفيد للبيانات التي تتغير نادرًا وليست الحداثة فيها حرجة.
  • إعادة التحقق عند الطلب: إعادة التحقق من البيانات بناءً على حدث (مثل تقديم نموذج). يمكن أن تستخدم إعادة التحقق عند الطلب نهجًا يعتمد على العلامات أو المسار لإعادة التحقق من مجموعات البيانات دفعة واحدة. هذا مفيد عندما تريد التأكد من عرض أحدث البيانات في أسرع وقت ممكن (مثل عند تحديث المحتوى من نظام إدارة المحتوى الخاص بك).

إعادة التحقق الزمنية

لإعادة التحقق من البيانات على فترات زمنية، يمكنك استخدام خيار next.revalidate لـ fetch لتعيين عمر ذاكرة التخزين المؤقت للمورد (بالثواني).

// إعادة التحقق على الأكثر كل ساعة
fetch('https://...', { next: { revalidate: 3600 } })

بدلاً من ذلك، يمكنك استخدام خيارات تكوين قطاع الطريق لتكوين جميع طلبات fetch في قطاع أو للحالات التي لا يمكنك فيها استخدام fetch.

كيف تعمل إعادة التحقق الزمنية

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

هذا مشابه لسلوك stale-while-revalidate.

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

يمكن إعادة التحقق من البيانات عند الطلب عن طريق المسار (revalidatePath) أو عن طريق علامة ذاكرة التخزين المؤقت (revalidateTag).

كيف تعمل إعادة التحقق عند الطلب

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

عدم المشاركة

لطلبات جلب البيانات الفردية، يمكنك عدم المشاركة في التخزين المؤقت عن طريق تعيين خيار cache إلى no-store. هذا يعني أنه سيتم جلب البيانات كلما تم استدعاء fetch.

// عدم المشاركة في التخزين المؤقت لطلب `fetch` فردي
fetch(`https://...`, { cache: 'no-store' })

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

// عدم المشاركة في التخزين المؤقت لجميع طلبات البيانات في قطاع الطريق
export const dynamic = 'force-dynamic'

ملاحظة: ذاكرة التخزين المؤقت للبيانات متاحة حاليًا فقط في الصفحات/الطرق، وليس في middleware. أي عمليات جلب تتم داخل middleware الخاص بك ستكون غير مخزنة افتراضيًا.

ذاكرة التخزين المؤقت للبيانات في Vercel

إذا تم نشر تطبيق Next.js الخاص بك على Vercel، نوصي بقراءة وثائق ذاكرة التخزين المؤقت للبيانات في Vercel لفهم أفضل للميزات الخاصة بـ Vercel.

ذاكرة التخزين المؤقت الكاملة للطريق (Full Route Cache)

المصطلحات ذات الصلة:

قد ترى مصطلحات التحسين الثابت التلقائي أو توليد الموقع الثابت أو التصيير الثابت تُستخدم بالتبادل للإشارة إلى عملية تصيير وتخزين طرق تطبيقك مؤقتًا في وقت البناء.

يُصيِّر Next.js ويخزن الطرق مؤقتًا تلقائيًا في وقت البناء. هذا تحسين يسمح لك بتقديم الطريق المخزن مؤقتًا بدلاً من التصيير على الخادم لكل طلب، مما يؤدي إلى تحميل أسرع للصفحات.

لفهم كيفية عمل ذاكرة التخزين المؤقت الكاملة للطريق، من المفيد النظر في كيفية تعامل React مع التصيير، وكيف يخزن Next.js النتيجة:

1. تصيير React على الخادم

على الخادم، يستخدم Next.js واجهات برمجة تطبيقات React لتنظيم التصيير. يتم تقسيم عمل التصيير إلى أجزاء: حسب أجزاء الطريق الفردية وحدود Suspense.

يتم تصيير كل جزء في خطوتين:

  1. يُصيِّر React مكونات الخادم إلى تنسيق بيانات خاص مُحسَّن للبث، يُسمى React Server Component Payload.
  2. يستخدم Next.js React Server Component Payload وتعليمات JavaScript لمكونات العميل لتصيير HTML على الخادم.

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

ما هو React Server Component Payload؟

React Server Component Payload هو تمثيل ثنائي مضغوط لشجرة مكونات خادم React المصيَّرة. يتم استخدامه بواسطة React على العميل لتحديث DOM للمتصفح. يحتوي React Server Component Payload على:

  • نتيجة تصيير مكونات الخادم
  • عناصر نائبة لمكان تصيير مكونات العميل ومراجع لملفات JavaScript الخاصة بها
  • أي خصائص تم تمريرها من مكون خادم إلى مكون عميل

لمعرفة المزيد، راجع وثائق مكونات الخادم.

2. تخزين Next.js مؤقتًا على الخادم (ذاكرة التخزين المؤقت الكاملة للطريق)

السلوك الافتراضي لذاكرة التخزين المؤقت الكاملة للطريق، يوضح كيفية تخزين React Server Component Payload و HTML على الخادم للطرق المصيَّرة بشكل ثابت.

السلوك الافتراضي لـ Next.js هو تخزين نتيجة التصيير (React Server Component Payload و HTML) لطريق على الخادم مؤقتًا. ينطبق هذا على الطرق المصيَّرة بشكل ثابت في وقت البناء، أو أثناء إعادة التحقق.

3. التهيئة (Hydration) والمصالحة (Reconciliation) لـ React على العميل

في وقت الطلب، على جانب العميل:

  1. يتم استخدام HTML لعرض معاينة أولية سريعة غير تفاعلية لمكونات العميل والخادم.
  2. يتم استخدام حمولة مكونات خادم React (React Server Components Payload) لمصالحة أشجار مكونات العميل ومكونات الخادم المُعَرضة، وتحديث DOM.
  3. يتم استخدام تعليمات JavaScript لـتهيئة مكونات العميل وجعل التطبيق تفاعليًا.

4. التخزين المؤقت في Next.js على العميل (ذاكرة التوجيه Router Cache)

يتم تخزين حمولة مكونات خادم React في ذاكرة التوجيه على جانب العميل - وهي ذاكرة تخزين مؤقت منفصلة في الذاكرة، مقسمة حسب أجزاء المسار الفردية. تُستخدم ذاكرة التوجيه هذه لتحسين تجربة التنقل عن طريق تخزين المسارات التي تمت زيارتها مسبقًا وجلب المسارات المستقبلية مسبقًا.

5. عمليات التنقل اللاحقة

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

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

العرض الثابت والديناميكي

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

يوضح هذا الرسم البياني الفرق بين المسارات المعروضة بشكل ثابت وديناميكي، مع البيانات المخزنة مؤقتًا وغير المخزنة:

كيف يؤثر العرض الثابت والديناميكي على ذاكرة المسار الكاملة. يتم تخزين المسارات الثابتة مؤقتًا في وقت البناء أو بعد إعادة التحقق من البيانات، بينما لا يتم تخزين المسارات الديناميكية مؤقتًا أبدًا

تعرف على المزيد حول العرض الثابت والديناميكي.

المدة

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

الإبطال

هناك طريقتان يمكنك من خلالهما إبطال ذاكرة المسار الكاملة:

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

الانسحاب

يمكنك الانسحاب من ذاكرة المسار الكاملة، أو بعبارة أخرى، عرض المكونات ديناميكيًا لكل طلب وارد، عن طريق:

  • استخدام وظيفة ديناميكية: سيؤدي هذا إلى إخراج المسار من ذاكرة المسار الكاملة وعرضه ديناميكيًا في وقت الطلب. لا يزال يمكن استخدام ذاكرة البيانات.
  • استخدام خيارات تكوين جزء المسار dynamic = 'force-dynamic' أو revalidate = 0: سيؤدي هذا إلى تخطي ذاكرة المسار الكاملة وذاكرة البيانات. مما يعني أنه سيتم عرض المكونات وجلب البيانات في كل طلب وارد إلى الخادم. ستظل ذاكرة التوجيه سارية لأنها ذاكرة على جانب العميل.
  • الانسحاب من ذاكرة البيانات: إذا كان للمسار طلب fetch غير مخزن مؤقتًا، فسيؤدي هذا إلى إخراج المسار من ذاكرة المسار الكاملة. سيتم جلب البيانات لطلب fetch المحدد لكل طلب وارد. سيتم تخزين طلبات fetch الأخرى التي لا تنسحب من التخزين المؤقت في ذاكرة البيانات. وهذا يسمح بمزيج من البيانات المخزنة وغير المخزنة.

ذاكرة التوجيه (Router Cache)

المصطلحات ذات الصلة:

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

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

كيف تعمل ذاكرة التوجيه

كيف تعمل ذاكرة التوجيه للمسارات الثابتة والديناميكية، مع عرض MISS و HIT للتنقلات الأولية واللاحقة.

عندما يتنقل المستخدم بين المسارات، يقوم Next.js بتخزين أجزاء المسار التي تمت زيارتها وجلب المسارات التي من المحتمل أن ينتقل إليها المستخدم مسبقًا (بناءً على مكونات <Link> في نطاق الرؤية).

هذا يؤدي إلى تحسين تجربة التنقل للمستخدم:

  • تنقل فوري للخلف/الأمام لأن المسارات التي تمت زيارتها مخزنة مؤقتًا وتنقل سريع إلى المسارات الجديدة بسبب الجلب المسبق والعرض الجزئي.
  • لا يوجد إعادة تحميل كاملة للصفحة بين عمليات التنقل، ويتم الحفاظ على حالة React وحالة المتصفح.

الفرق بين ذاكرة التوجيه وذاكرة المسار الكاملة:

تقوم ذاكرة التوجيه بتخزين حمولة مكونات خادم React مؤقتًا في المتصفح طوال مدة جلسة المستخدم، بينما تقوم ذاكرة المسار الكاملة بتخزين حمولة مكونات خادم React و HTML بشكل دائم على الخادم عبر طلبات متعددة للمستخدم.

بينما تقوم ذاكرة المسار الكاملة بتخزين المسارات المعروضة بشكل ثابت فقط، تنطبق ذاكرة التوجيه على كل من المسارات المعروضة بشكل ثابت وديناميكي.

المدة

يتم تخزين الذاكرة المؤقتة في الذاكرة المؤقتة للمتصفح. هناك عاملان يحددان المدة التي تستمر فيها ذاكرة التوجيه:

  • الجلسة: تستمر الذاكرة المؤقتة عبر التنقل. ومع ذلك، يتم مسحها عند تحديث الصفحة.
  • فترة الإبطال التلقائي: يتم إبطال الذاكرة المؤقتة للتخطيطات وحالات التحميل تلقائيًا بعد وقت محدد. تعتمد المدة على كيفية الجلب المسبق للمورد، وما إذا كان المورد تم إنشاؤه بشكل ثابت:
    • الجلب المسبق الافتراضي (prefetch={null} أو غير محدد): غير مخزن للمسارات الديناميكية، 5 دقائق للمسارات الثابتة.
    • الجلب المسبق الكامل (prefetch={true} أو router.prefetch): 5 دقائق لكل من المسارات الثابتة والديناميكية.

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

من الجيد معرفة: يمكن استخدام خيار التكوين التجريبي staleTimes لضبط أوقات الإبطال التلقائي المذكورة أعلاه.

الإبطال

هناك طريقتان يمكنك من خلالهما إبطال ذاكرة التوجيه:

  • في إجراء خادم (Server Action):
    • إعادة التحقق من البيانات حسب الطلب عن طريق المسار باستخدام (revalidatePath) أو عن طريق علامة التخزين المؤقت باستخدام (revalidateTag)
    • استخدام cookies.set أو cookies.delete يبطل ذاكرة التوجيه لمنع المسارات التي تستخدم ملفات تعريف الارتباط من أن تصبح قديمة (مثل المصادقة).
  • استدعاء router.refresh سوف يبطل ذاكرة التوجيه ويجعل طلبًا جديدًا إلى الخادم للمسار الحالي.

الانسحاب

ليس من الممكن الانسحاب من ذاكرة التوجيه. ومع ذلك، يمكنك إبطالها عن طريق استدعاء router.refresh، أو revalidatePath، أو revalidateTag (انظر أعلاه). سيؤدي هذا إلى مسح الذاكرة المؤقتة وإجراء طلب جديد إلى الخادم، مما يضمن عرض أحدث البيانات.

يمكنك أيضًا الانسحاب من الجلب المسبق عن طريق تعيين خاصية prefetch لمكون <Link> إلى false. ومع ذلك، سيظل هذا يقوم بتخزين أجزاء المسار مؤقتًا لمدة 30 ثانية للسماح بالتنقل الفوري بين الأجزاء المتداخلة، مثل أشرطة التبويب، أو التنقل للخلف والأمام. سيتم تخزين المسارات التي تمت زيارتها مؤقتًا.

تفاعلات الذاكرة المؤقتة

عند تكوين آليات التخزين المؤقت المختلفة، من المهم فهم كيفية تفاعلها مع بعضها البعض:

ذاكرة البيانات وذاكرة المسار الكاملة

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

ذاكرة البيانات وذاكرة التوجيه على العميل

  • إعادة التحقق من ذاكرة البيانات في معالج المسار (Route Handler) لن يبطل ذاكرة التوجيه على الفور لأن معالج المسار غير مرتب بمسار محدد. وهذا يعني أن ذاكرة التوجيه ستستمر في تقديم الحمولة السابقة حتى تحديث قوي، أو انتهاء فترة الإبطال التلقائي.
  • لإبطال ذاكرة البيانات وذاكرة التوجيه على الفور، يمكنك استخدام revalidatePath أو revalidateTag في إجراء خادم (Server Action).

واجهات برمجة التطبيقات (APIs)

يوضح الجدول التالي نظرة عامة على كيفية تأثير واجهات برمجة التطبيقات المختلفة لـ Next.js على التخزين المؤقت:

الواجهة البرمجيةذاكرة التوجيهذاكرة المسار الكاملةذاكرة البياناتذاكرة React
<Link prefetch>تخزين مؤقت
router.prefetchتخزين مؤقت
router.refreshإعادة التحقق
fetchتخزين مؤقتتخزين مؤقت
fetch options.cacheتخزين مؤقت أو انسحاب
fetch options.next.revalidateإعادة التحققإعادة التحقق
fetch options.next.tagsتخزين مؤقتتخزين مؤقت
revalidateTagإعادة التحقق (إجراء خادم)إعادة التحققإعادة التحقق
revalidatePathإعادة التحقق (إجراء خادم)إعادة التحققإعادة التحقق
const revalidateإعادة التحقق أو انسحابإعادة التحقق أو انسحاب
const dynamicتخزين مؤقت أو انسحابتخزين مؤقت أو انسحاب
cookiesإعادة التحقق (إجراء خادم)انسحاب
headers, searchParamsانسحاب
generateStaticParamsتخزين مؤقت
React.cacheتخزين مؤقت
unstable_cache

افتراضيًا، يقوم مكون <Link> تلقائيًا بجلب المسارات مسبقًا من ذاكرة المسار الكاملة وإضافة حمولة مكونات خادم React إلى ذاكرة التوجيه.

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

تعرف على المزيد حول مكون <Link>.

router.prefetch

يمكن استخدام خيار prefetch في خطاف useRouter لجلب مسار مسبقًا يدويًا. هذا يضيف حمولة مكونات خادم React إلى ذاكرة التوجيه.

راجع مرجع خطاف useRouter.

router.refresh

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

سيتم مصالحة النتيجة المعروضة على العميل مع الحفاظ على حالة React وحالة المتصفح.

راجع مرجع خطاف useRouter.

fetch

يتم تخزين البيانات المرتجعة من fetch تلقائيًا في ذاكرة البيانات.

// مخزن مؤقتًا افتراضيًا. `force-cache` هو الخيار الافتراضي ويمكن حذفه.
fetch(`https://...`, { cache: 'force-cache' })

راجع مرجع واجهة fetch لمزيد من الخيارات.

fetch options.cache

يمكنك الانسحاب من تخزين طلبات fetch الفردية للبيانات عن طريق تعيين خيار cache إلى no-store:

// الانسحاب من التخزين المؤقت
fetch(`https://...`, { cache: 'no-store' })

نظرًا لأن ناتج العرض يعتمد على البيانات، فإن استخدام cache: 'no-store' سيتخطى أيضًا ذاكرة المسار الكاملة للمسار الذي يتم فيه استخدام طلب fetch. أي أنه سيتم عرض المسار ديناميكيًا في كل طلب، ولكن لا يزال بإمكانك الحصول على طلبات بيانات مخزنة أخرى في نفس المسار.

راجع مرجع واجهة fetch لمزيد من الخيارات.

fetch options.next.revalidate

يمكنك استخدام خيار next.revalidate في fetch لتعيين فترة إعادة التحقق (بالثواني) لطلب fetch فردي. سيعيد هذا التحقق من ذاكرة البيانات، مما سيعيد بدوره التحقق من ذاكرة المسار الكاملة. سيتم جلب بيانات جديدة، وسيتم إعادة عرض المكونات على الخادم.

// إعادة التحقق على الأكثر بعد ساعة واحدة
fetch(`https://...`, { next: { revalidate: 3600 } })

راجع مرجع واجهة fetch لمزيد من الخيارات.

fetch options.next.tags و revalidateTag

يقدم Next.js نظامًا لوضع علامات على ذاكرة التخزين المؤقت للتحكم الدقيق في تخزين البيانات وإعادة التحقق منها.

  1. عند استخدام fetch أو unstable_cache، يمكنك وضع علامة واحدة أو أكثر على مدخلات ذاكرة التخزين المؤقت.
  2. ثم يمكنك استدعاء revalidateTag لمسح مدخلات ذاكرة التخزين المؤقت المرتبطة بتلك العلامة.

على سبيل المثال، يمكنك تعيين علامة عند جلب البيانات:

// تخزين البيانات مع علامة
fetch(`https://...`, { next: { tags: ['a', 'b', 'c'] } })

ثم استدعاء revalidateTag مع علامة لمسح مدخل ذاكرة التخزين المؤقت:

// إعادة التحقق من المدخلات ذات العلامة المحددة
revalidateTag('a')

هناك مكانان يمكنك استخدام revalidateTag فيهما، حسب ما تحاول تحقيقه:

  1. معالجات المسارات (Route Handlers) - لإعادة التحقق من البيانات استجابةً لحدث طرف ثالث (مثل webhook). لن يؤدي هذا إلى إبطال ذاكرة تخزين المسار (Router Cache) فورًا لأن معالج المسار غير مرتبط بمسار محدد.
  2. الإجراءات الخادمية (Server Actions) - لإعادة التحقق من البيانات بعد إجراء المستخدم (مثل إرسال نموذج). سيؤدي هذا إلى إبطال ذاكرة تخزين المسار للمسار المرتبط.

revalidatePath

يسمح لك revalidatePath بإعادة التحقق من البيانات يدويًا و إعادة عرض مقاطع المسار أسفل مسار محدد في عملية واحدة. يؤدي استدعاء دالة revalidatePath إلى إعادة التحقق من ذاكرة تخزين البيانات (Data Cache)، مما يؤدي بدوره إلى إبطال ذاكرة تخزين المسار الكاملة (Full Route Cache).

revalidatePath('/')

هناك مكانان يمكنك استخدام revalidatePath فيهما، حسب ما تحاول تحقيقه:

  1. معالجات المسارات (Route Handlers) - لإعادة التحقق من البيانات استجابةً لحدث طرف ثالث (مثل webhook).
  2. الإجراءات الخادمية (Server Actions) - لإعادة التحقق من البيانات بعد تفاعل المستخدم (مثل إرسال نموذج، النقر على زر).

راجع مرجع واجهة برمجة التطبيقات لـ revalidatePath لمزيد من المعلومات.

مقارنة بين revalidatePath و router.refresh:

يؤدي استدعاء router.refresh إلى مسح ذاكرة تخزين المسار (Router Cache)، وإعادة عرض مقاطع المسار على الخادم دون إبطال ذاكرة تخزين البيانات (Data Cache) أو ذاكرة تخزين المسار الكاملة (Full Route Cache).

الفرق هو أن revalidatePath يمسح ذاكرة تخزين البيانات وذاكرة تخزين المسار الكاملة، بينما router.refresh() لا يغير ذاكرة تخزين البيانات وذاكرة تخزين المسار الكاملة، لأنه واجهة برمجة تطبيقات من جانب العميل.

الدوال الديناميكية

تعتمد الدوال الديناميكية مثل cookies و headers، وخاصية searchParams في الصفحات على معلومات الطلب الواردة أثناء التشغيل. يؤدي استخدامها إلى استبعاد المسار من ذاكرة تخزين المسار الكاملة، بمعنى آخر، سيتم عرض المسار ديناميكيًا.

cookies

يؤدي استخدام cookies.set أو cookies.delete في إجراء خادمي (Server Action) إلى إبطال ذاكرة تخزين المسار (Router Cache) لمنع المسارات التي تستخدم ملفات تعريف الارتباط من أن تصبح قديمة (على سبيل المثال، لتعكس تغييرات المصادقة).

راجع مرجع واجهة برمجة التطبيقات لـ cookies.

خيارات تكوين المقاطع

يمكن استخدام خيارات تكوين مقطع المسار (Route Segment Config) لتجاوز الإعدادات الافتراضية لمقطع المسار أو عندما لا يمكنك استخدام واجهة برمجة التطبيقات fetch (على سبيل المثال، عميل قاعدة بيانات أو مكتبات طرف ثالث).

ستؤدي خيارات تكوين مقطع المسار التالية إلى استبعاد المسار من ذاكرة تخزين البيانات وذاكرة تخزين المسار الكاملة:

  • const dynamic = 'force-dynamic'
  • const revalidate = 0

راجع توثيق تكوين مقاطع المسار (Route Segment Config) لمزيد من الخيارات.

generateStaticParams

بالنسبة للمقاطع الديناميكية (على سبيل المثال، app/blog/[slug]/page.js)، يتم تخزين المسارات المقدمة من generateStaticParams في ذاكرة تخزين المسار الكاملة (Full Route Cache) وقت البناء. عند وقت الطلب، سيخزن Next.js أيضًا المسارات التي لم تكن معروفة وقت البناء عند زيارتها لأول مرة.

يمكنك تعطيل التخزين المؤقت عند وقت الطلب باستخدام خيار export const dynamicParams = false في مقطع المسار. عند استخدام خيار التكوين هذا، سيتم تقديم المسارات المقدمة من generateStaticParams فقط، وستؤدي المسارات الأخرى إلى ظهور خطأ 404 أو تطابق (في حالة المقاطع الشاملة (catch-all routes)).

راجع مرجع واجهة برمجة التطبيقات لـ generateStaticParams.

دالة React cache

تسمح لك دالة React cache بحفظ قيمة الإرجاع للدالة، مما يتيح لك استدعاء نفس الدالة عدة مرات مع تنفيذها مرة واحدة فقط.

نظرًا لأن طلبات fetch يتم حفظها تلقائيًا، فلا تحتاج إلى تغليفها في React cache. ومع ذلك، يمكنك استخدام cache لحفظ طلبات البيانات يدويًا للحالات التي لا تكون فيها واجهة برمجة التطبيقات fetch مناسبة. على سبيل المثال، بعض عملاء قواعد البيانات، أو عملاء أنظمة إدارة المحتوى (CMS)، أو عملاء GraphQL.

import { cache } from 'react'
import db from '@/lib/db'

export const getItem = cache(async (id: string) => {
  const item = await db.item.findUnique({ id })
  return item
})
import { cache } from 'react'
import db from '@/lib/db'

export const getItem = cache(async (id) => {
  const item = await db.item.findUnique({ id })
  return item
})

On this page