الفرق بين المراجعتين لصفحة: «Next.js/data fetching»

من موسوعة حسوب
لا ملخص تعديل
 
(12 مراجعة متوسطة بواسطة مستخدمين اثنين آخرين غير معروضة)
سطر 1: سطر 1:
<noinclude>{{DISPLAYTITLE:إحضار البيانات في Next.js}}</noinclude>
<noinclude>{{DISPLAYTITLE:إحضار البيانات في Next.js}}</noinclude>
تتيح عملية إحضار البيانات في Next.js تصيير المحتوى بطرق عدة وفقًا للحالة التي تستخدم فيها التطبيق، بما في ذلك التصيير المسبق من جانب الخادم Server-side Rendering أو التوليد الساكن للشيفرة Static Generation أو تحديث المحتوى أو إنشائه أثناء تشغيل التطبيق من خلال التوليد الساكن التدريجي Incremental Static Regeneration.
تتيح عملية إحضار البيانات في Next.js تصيير المحتوى بطرق عدة وفقًا للحالة التي تستخدم فيها التطبيق، بما في ذلك التصيير المسبق من جانب الخادم Server-Side Rendering أو التوليد الساكن للشيفرة Static Generation أو تحديث المحتوى أو إنشائه أثناء تشغيل التطبيق من خلال التوليد الساكن التدريجي Incremental Static Regeneration.


== التصيير من جانب الخادم في Next.js باستخدام الدالة getServerSideProps ==
== الدالة غير المتزامنة <code>getInitialProps</code> ==
عندما تُصدِّر دالة تُدعى <code>getServerSideProps</code> (التصيير من جانب الخادم SSR) من صفحتك، تُصدَّر الصفحة مسبقًا عند كل طلب باستخدام البيانات التي تعيدها الدالة <code>getServerSideProps</code>:<syntaxhighlight>
'''نصيحة هامة''': يتيح لك استخدام إحدى الدالتين [[Next.js/data fetching#.D8.A7.D9.84.D8.AA.D9.88.D9.84.D9.8A.D8.AF%20.D8.A7.D9.84.D8.B3.D8.A7.D9.83.D9.86%20.D9.84.D9.84.D8.B5.D9.81.D8.AD.D8.A7.D8.AA%20.D9.81.D9.8A%20Next.js%20.D8.A8.D8.A7.D8.B3.D8.AA.D8.AE.D8.AF.D8.A7.D9.85%20.D8.A7.D9.84.D8.AF.D8.A7.D9.84.D8.A9%20getStaticProps|<code>getStaticProps</code>]] أو [[Next.js/data fetching#.D8.A7.D8.B3.D8.AA.D8.AE.D8.AF.D8.A7.D9.85%20.D8.A7.D9.84.D8.AF.D8.A7.D9.84.D8.A9%20getServerSideProps%20.D9.84.D8.A5.D8.AD.D8.B6.D8.A7.D8.B1%20.D8.A7.D9.84.D8.A8.D9.8A.D8.A7.D9.86.D8.A7.D8.AA%20.D8.B9.D9.86.D8.AF%20.D8.A7.D9.84.D8.B7.D9.84.D8.A8%20.D9.81.D9.8A%20Next.js|<code>getServerSideProps</code>]] بدلًا من  <code>getInitialProps</code> في إحضار البيانات أسلوبًا يجمع بين التوليد الساكن والتصيير من جانب الخادم.
 
'''ملاحظة:''' قدّمت النسخة 13 من Next.js مجلدًا جديدًا يُدعى <code>app/</code>. ويدعم هذا المجلد إحضار البيانات من نفس المكان على صعيد المكوًنات باستخدام خطاف React الجديد <code>use</code> وواجهة الويب البرمجية الموّسعة <code>fetch</code>
 
تُمكّنك الدالة <code>getInitialProps</code> من تصيير المحتوى من جانب الخادم في [[Next.js/pages|الصفحة]]، وتسمح لك بنقل البيانات بشكل أولي، أي إرسال الصفحة بالبيانات التي أنتجها الخادم، وهذا مفيد خصوصًا في تحسين محركات البحث SEO.
 
'''ملاحظة''': يُعطل استخدام الدالة <code>getInitialProps</code> [[Next.js/automatic static optimization|التحسين التلقائي الساكن]].
 
تُعد الدالة <code>getInitialProps</code> دالة غير متزامنة <code>async</code> يمكن إضافتها إلى أي صفحة كتابع ساكن <code>static method</code>. إليك مثالًا:<syntaxhighlight lang="javascript">
function Page({ stars }) {
  return <div>Next stars: {stars}</div>
}
 
Page.getInitialProps = async (ctx) => {
  const res = await fetch('https://api.github.com/repos/vercel/next.js')
  const json = await res.json()
  return { stars: json.stargazers_count }
}
 
export default Page
</syntaxhighlight>أو باستخدام مكوّن صنف:<syntaxhighlight lang="javascript">
import React from 'react'
 
class Page extends React.Component {
  static async getInitialProps(ctx) {
    const res = await fetch('https://api.github.com/repos/vercel/next.js')
    const json = await res.json()
    return { stars: json.stargazers_count }
  }
 
  render() {
    return <div>Next stars: {this.props.stars}</div>
  }
}
 
export default Page
</syntaxhighlight>
 
 
تُستخدم الدالة <code>getInitialProps</code> لإحضار بعض البيانات بشكل غير متزامن لتحلل بعدها البيانات الجاهزة المعادة <code>props</code> من الدالة أثناء التصيير في الواجهة الخلفية بشكل مشابه لما يفعله التابع
 
<code>JSON.stringify</code>. تأكد أن الكائن الذي تعيده الدالة <code>getInitialProps</code> هو كائن بسيط لا يحتوي على الخاصيات <code>Date</code> أو <code>Map</code> أو <code>Set</code>.
 
تنفذ الدالة <code>getInitialProps</code> على الخادم فقط لتحميل الصفحة مبدئيًا، ثم تنفذ في الواجهة الأمامية عند الانتقال إلى وجهة أخرى باستخدام المكون [[Next.js/next link|<code>next/link</code>]] أو المكون [[Next.js/next router|<code>next/router</code>]]. لكن إن استخدمت الدالة <code>getInitialProps</code> تطبيقًا مخصصًا  <code>app.js_</code> وتضمنت الصفحة التي سننتقل إليها الدالة <code>getServerSideProps</code> عندها لن تعمل الدالة <code>getInitialProps</code> على الخادم.
 
=== الكائن <code>context</code> ===
تتلقى الدالة <code>getInitialProps</code> وسيطًا واحدًا يدعى <code>context</code>، وهو كائن يمتلك الخاصيات التالية:
 
* <code>pathname</code>: الوجهة الحالية، وهي مسار الصفحة ضمن المجلد <code>pages/</code>.
* <code>query</code>: قسم الاستعلام النصي من عنوان URL والمُحلَّل ككائن.
* <code>asPath</code>: من النوع <code>String</code>، يمثل المسار الفعلي (بما في ذلك الاستعلام) الظاهر في شريط عنوان المتصفح.
* <code>req</code>: [[Node.js/http|كائن طلب HTTP]] (خادم فقط).
* <code>res</code>: [[Node.js/http|كائن طلب استجابة HTTP]] (خادم فقط).
* <code>err</code>: كائن خطأ وينتج عن أي خطا يحدث أثناء التصيير.
 
=== التحفظات على استخدام الدالة ===
 
* لا يمكن استخدام الدالة <code>getInitialProps</code> داخل المكوّنات الأبناء، بل فقط عند التصدير الافتراضي لكل صفحة.
* إن كنت تستخدم وحدات الواجهة الخلفية فقط داخل الدالة <code>getInitialProps</code>، فتأكد من إدراجها بشكل صحيح وإلا ستبطئ تطبيقك.
 
'''ملاحظة:''' بغض النظر عن نوع التصيير ستُمرر أية خاصية <code>props</code> إلى مكون الصفحة وتُستعرض في الواجهة الأمامية كجزء من صفحة HTML المبدئية. إن الغاية من ذلك هو السماح [[React/react dom#hydrate.28.29|بترطيب الصفحة]] hydrate بالشكل الصحيح. تأكد من عدم تمرير أية معلومات حساسة لا ينبغي عرضها في الواجهة الأمامية من خلال الخاصيات <code>props</code>.
 
=== استخدام الدالة مع TypeScript ===
إن كنت تستخدم TypeScript، بإمكانك استخدام النوع <code>NextPage</code> من أجل مكونات الدوال: <syntaxhighlight lang="javascript">
import { NextPage } from 'next'
 
interface Props {
  userAgent?: string;
}
 
const Page: NextPage<Props> = ({ userAgent }) => (
  <main>Your user agent: {userAgent}</main>
)
 
Page.getInitialProps = async ({ req }) => {
  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent
  return { userAgent }
}
 
export default Page
</syntaxhighlight>ويمكنك استخدام <code>NextPageContext</code> من أجل <code>React.Component</code> :<syntaxhighlight lang="javascript">
import React from 'react'
import { NextPageContext } from 'next'
 
interface Props {
  userAgent?: string;
}
 
export default class Page extends React.Component<Props> {
  static async getInitialProps({ req }: NextPageContext) {
    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent
    return { userAgent }
  }
 
  render() {
    const { userAgent } = this.props
    return <main>Your user agent: {userAgent}</main>
  }
}
</syntaxhighlight>
 
== التصيير من جانب الخادم باستخدام الدالة <code>getServerSideProps</code> ==
عندما تُصدِّر دالة تُدعى <code>getServerSideProps</code> (التصيير من جانب الخادم SSR) من صفحتك، تُصدَّر الصفحة مسبقًا عند كل طلب باستخدام البيانات التي تعيدها الدالة <code>getServerSideProps</code>:<syntaxhighlight lang="javascript">
export async function getServerSideProps(context) {
export async function getServerSideProps(context) {
   return {
   return {
سطر 9: سطر 111:
   }
   }
}
}
</syntaxhighlight>
</syntaxhighlight>'''ملاحظة''': بغض النظر عن نوع التصيير، أي شيء يُمرر إلى مكون الصفحة page ضمن الخاصيات props يُعرَض في طرف العميل ضمن صفحة HTML، وصحيح هذا يساعد على ترطيب الصفحة وتغذيها بالبيانات والمعلومات، ولكن كن حذرًا من تمرير أي بيانات حساسة لا يجب أن تُمرر إلى طرف العميل.


=== تنفيذ الدالة getServerSideProps في Next.js ===
=== تنفيذ الدالة <code>getServerSideProps</code> ===
تُنفَّذ هذه الدالة من جانب الخادم ولا يمكن تنفيذها من قبل المتصفح، وما يحدث عندما تستخدمها الصفحة أنه:
تُنفَّذ هذه الدالة من جانب الخادم ولا يمكن تنفيذها من قبل المتصفح، وما يحدث عندما تستخدمها الصفحة أنه:


* ستُنفَّذ الدالة <code>getServerSideProps</code> عند الطلب، وذلك عندما تطلب الصفحة مباشرة، وستصيّر مسبقًا مع الخاصيات التي تعيدها الدالة.
* عندما تطلب الصفحة مباشرة، ستُنفَّذ الدالة <code>getServerSideProps</code> وستُصيّر مسبقًا مع الخاصيات التي تعيدها الدالة.
* سترسل Next.js طلبًا من خلال واجهة برمحية API إلى الخادم لكي ينفِّذ الدالة <code>getServerSideProps</code>، وذلك عندما تطلب الصفحة من جانب العميل من خلال <code>next/link</code> أو <code>next/router</code>.
* عندما تطلب الصفحة من جانب العميل من خلال <code>next/link</code> أو <code>next/router</code>، سترسل Next.js طلبًا من خلال واجهة برمحية API إلى الخادم لكي ينفِّذ الدالة <code>getServerSideProps</code>.
 
تعيد الدالة <code>getServerSideProps</code> النتيجة على شكل بيانات بصيغة JSON تُستخدم في تصيير الصفحة، وستنجز Next.js كل هذه الأعمال تلقائيًا دون أن تفعل أي شيء إضافي عندما تُعرّف هذه الدالة.
 
يمكنك استخدام الأداة [https://next-code-elimination.vercel.app/ next-code-elimination tool] للتحقق من شيفرة Next.js التي تُحذف عندما تُجمّع للعمل من ناحية العميل.


تعيد الدالة <code>getServerSideProps</code> النتيجة على شكل بيانات JSON تُستخدم في تصيير الصفحة، وستنجز  Next.js كل هذه الأعمال تلقائيًا دون أن تفعل أي شيء إضافي عندما تُعرّف هذه الدالة.<blockquote>يمكنك استخدام الأداة [https://next-code-elimination.vercel.app/ next-code-elimination tool] للتحقق من شيفرة Next.js التي تُحذف عندما تُجمّع للعمل من ناحية العميل.</blockquote>تُصدَّر الدالة <code>getServerSideProps</code> من الصفحات فقط، ولا يمكن تصديرها من ملفات لا تُعدُّ صفحاتٍ. و ينبغي الانتباه إلى تصديرها كدالة قائمة بحد ذاتها standalone، فلن تعمل إن أضفتها كخاصية من خواص مكوّن الصفحة.
تُصدَّر الدالة <code>getServerSideProps</code> من [[Next.js/pages|الصفحات]] فقط، ولا يمكن تصديرها من ملفات لا تُعدُّ صفحاتٍ. وينبغي الانتباه إلى تصديرها كدالة قائمة بحد ذاتها standalone، فلن تعمل إن أضفتها كخاصية من خواص مكوّن الصفحة.


يمكنك معرفة جميع المعاملات والخاصيات التي تُستخدم مع <code>getServerSideProps</code>، بالاطلاع على [[Next.js/api data fetching|مرجع الواجهة البرمجية]] API reference.
يمكنك معرفة جميع المعاملات والخاصيات التي تُستخدم مع <code>getServerSideProps</code>، بالاطلاع على [[Next.js/api data fetching|مرجع الواجهة البرمجية]] API reference.


=== حالات استخدام الدالة getServerSideProps في Next.js ===
=== حالات استخدام الدالة <code>getServerSideProps</code> ===
استخدم هذه الدالة إن أردت أن تصيّر صفحة من المفترض إحضار بياناتها عند طلب هذه الصفحة. وقد يكون السبب هو طبيعة البيانات أو خصائص الطلب (مثل ترويسات الاستيثاق <code>authorization</code> أو الموقع الجغرافي). ستُصيّر الصفحات التي تستخدم <code>getServerSideProps</code> من جانب الخادم أثناء الطلب ولن تُخزَّن مؤقتًا ما لم تُهيَّأ [[Next.js/Going to Production|ترويسات التحكم بالتخزين المؤقت]].
استخدم هذه الدالة إن أردت أن تصيّر صفحة من المفترض إحضار بياناتها عند طلب هذه الصفحة. وقد يكون السبب هو طبيعة البيانات أو خصائص الطلب (مثل ترويسات الاستيثاق <code>authorization</code> أو الموقع الجغرافي). ستُصيّر الصفحات التي تستخدم <code>getServerSideProps</code> من جانب الخادم أثناء الطلب ولن تُخزَّن مؤقتًا ما لم تُهيَّأ [[Next.js/going to production|ترويسات التحكم بالتخزين المؤقت]].


إن لم تشأ أن تصير البيانات أثناء الطلب، فقد تفكّر في إحضارها من ناحية العميل أو باستخدام الدالة <code>getStaticProps</code>.
إن لم تشأ أن تصير البيانات أثناء الطلب، فقد تفكّر في إحضارها من ناحية العميل أو باستخدام الدالة <code>getStaticProps</code>.


=== استخدام الدالة getServerSideProps مقابل مسارات الواجهة البرمجية API Route ===
=== استخدام الدالة <code>getServerSideProps</code> مقابل مسارات الواجهة البرمجية API Route ===
قد يكون العثور على '''مسار API''' مغريًا لإحضار البيانات من الخادم بدلًا من استدعاء هذا المسار من خلال الدالة <code>getServerSideProps</code>. لكن هذا الأسلوب غير ضروري وغير مجدٍ، لأنه سينفِّذ طلبًا إضافيًا نظرًا لتنفيذ الدالة <code>getServerSideProps</code> مع المسار معًا على الخادم.
قد يكون تلجأ إلى أسلوب إنشاء [[Next.js/api routes|مسار API]] لإحضار البيانات من الخادم ثم استدعاء هذا المسار من خلال الدالة <code>getServerSideProps</code> لكن هذا الأسلوب غير ضروري وغير مجدٍ، لأنه سينفِّذ طلبًا إضافيًا نظرًا لتنفيذ الدالة <code>getServerSideProps</code> مع مسار الواجهة API معًا على الخادم.


لنفترض مثلًا وجود مسار API يُستخدم في إحضار بيانات من منظومة إدارة المحتوى CMS. يُستدعى هذا المسار عندها من قبل الدالة <code>getServerSideProps</code> مباشرة، وسينتج عن ذلك استدعاء إضافي يخفض الأداء. بدلًا من ذلك أدرج تعليمات المنطق الذي يعتمده مسار API ضمن الدالة مباشرة، وقد يكون هذا المنطق استدعاءً لمنظومة إدارة المحتوى أو قاعدة بيانات أو مسارات API أخرى.
لنفترض مثلًا وجود مسار API يُستخدم في إحضار بيانات من منظومة إدارة المحتوى CMS. يُستدعى هذا المسار عندها من قبل الدالة <code>getServerSideProps</code> مباشرة، وسينتج عن ذلك استدعاءً إضافيًا يخفض الأداء. بدلًا من ذلك أدرج تعليمات المنطق الذي يعتمده مسار API ضمن الدالة مباشرة، وقد يكون هذا المنطق استدعاءً لمنظومة إدارة المحتوى أو قاعدة بيانات أو مسارات API أخرى.


=== إحضار البيانات من جانب العميل في Next.js ===
=== إحضار البيانات من جانب العميل ===
إن احتوت صفحتك على بيانات تُحدَث باستمرار ولا حاجة لتصييرها مسبقًا، بإمكانك عندها إحضار البيانات من جانب العميل وكمثال على سيناريوهات كهذه إحضار بيانات خاصة بالعميل:
إن احتوت صفحتك على بيانات تُحدَث باستمرار ولا حاجة لتصييرها مسبقًا، بإمكانك عندها إحضار البيانات من جانب العميل وكمثال على سيناريوهات كهذه إحضار بيانات خاصة بالعميل:


سطر 39: سطر 145:
تعمل هذه المقاربة جيدًا مع الصفحات التي تمثل لوحة تحكم أو إعدادات لانها صفحات خاصة بالمستخدم لا حاجة لتطبيق قواعد تحسين محركات البحث عليها، ولا حاجة لتصييرها مسبقًا. تُحدَّث هذه البيانات باستمرار ويتطلب ذلك إحضار هذه البيانات عند الطلب فقط.
تعمل هذه المقاربة جيدًا مع الصفحات التي تمثل لوحة تحكم أو إعدادات لانها صفحات خاصة بالمستخدم لا حاجة لتطبيق قواعد تحسين محركات البحث عليها، ولا حاجة لتصييرها مسبقًا. تُحدَّث هذه البيانات باستمرار ويتطلب ذلك إحضار هذه البيانات عند الطلب فقط.


=== استخدام الدالة getServerSideProps لإحضار البيانات عند الطلب في Next.js ===
=== استخدام الدالة <code>getServerSideProps</code> لإحضار البيانات عند الطلب ===
يعرض المثال التالي كيفية إحضار البيانات عند الطلب ثم تصيير البيانات مسبقًا قبل عرضها:<syntaxhighlight lang="javascript">
يعرض المثال التالي كيفية إحضار البيانات عند الطلب ثم تصيير البيانات مسبقًا قبل عرضها:<syntaxhighlight lang="javascript">
function Page({ data }) {
function Page({ data }) {
سطر 47: سطر 153:
// تُستدعى هذه الشيفرة عند كل طلب
// تُستدعى هذه الشيفرة عند كل طلب
export async function getServerSideProps() {
export async function getServerSideProps() {
   // أحضر البيانات من وصلة برمحية خارجية
   // أحضر البيانات من وصلة برمجية خارجية
   const res = await fetch(`https://.../data`)
   const res = await fetch(`https://.../data`)
   const data = await res.json()
   const data = await res.json()
سطر 58: سطر 164:
</syntaxhighlight>
</syntaxhighlight>


=== التخزين المؤقت أثناء التصيير من جانب الخادم (SSR) ===
=== التخزين المؤقت أثناء التصيير من جانب الخادم SSR ===
بإمكانك استخدام ترويسات التخزين المؤقت (<code>Cache-Control</code>) ضمن الدالة <code>getServerSideProps</code> لتخزين الاستجابات الديناميكية مؤقتًا. استخدم القيمة <code>[https://web.dev/stale-while-revalidate/ stale-while-revalidate]</code> مثلًا:<syntaxhighlight>
بإمكانك استخدام ترويسات التخزين المؤقت <code>Cache-Control</code> ضمن الدالة <code>getServerSideProps</code> لتخزين الاستجابات الديناميكية مؤقتًا. استخدم القيمة <code>[https://web.dev/stale-while-revalidate/ stale-while-revalidate]</code> مثلًا:<syntaxhighlight lang="javascript">
// (s-maxage=10)تُعد هذه القيمة جديدة مدة عشر ثوان.
// (s-maxage=10)تُعد هذه القيمة جديدة مدة عشر ثوان.
// إن أعيد الطلب ضمن هذه الثواني العشرة ستبقى هذه القيمة حديثة
// إن أعيد الطلب ضمن هذه الثواني العشرة ستبقى هذه القيمة حديثة
// بينما إن أعيد الطلب قبل 59 ثانية ستُصيّر القيمة على الرغم من كونها قديمة
// بينما إن أعيد الطلب قبل 59 ثانية ستُصيّر القيمة على الرغم من كونها قديمة
//(stale-while-revalidate=59).
// (stale-while-revalidate=59).
//سيجري طلب خلف الستار لتحديث  هذه القيمة في الذاكرة المؤقتة  
// سيجري طلب خلف الستار لتحديث  هذه القيمة في الذاكرة المؤقتة  
//سترى هذه القيمة الجديدة إن حدّثت الصفحة  
// سترى هذه القيمة الجديدة إن حدّثت الصفحة  
export async function getServerSideProps({ req, res }) {
export async function getServerSideProps({ req, res }) {
   res.setHeader(
   res.setHeader(
سطر 78: سطر 184:
</syntaxhighlight>
</syntaxhighlight>


=== تصيير الأخطاء عند استخدام الدالة getServerSideProps في Next.js ===
=== تصيير الأخطاء عند استخدام الدالة <code>getServerSideProps</code> ===
إن ظهر خطأ داخل الدالة <code>getServerSideProps</code> فستعرض الملف <code>pages/500.js</code>. راجع [[Next.js/Custom Error Page|توثيق الصفحة 500]] لتتعلم طريقة إنشائها. لن يُستخدم هذا الملف في مرحلة التطوير وستُعرض الصفحة الخاصة بهذه المرحلة بدلًا منه.
إن ظهر خطأ داخل الدالة <code>getServerSideProps</code> فستعرض الملف <code>pages/500.js</code>. راجع [[Next.js/custom error page|توثيق الصفحة 500]] لتتعلم طريقة إنشائها. لن يُستخدم هذا الملف في مرحلة التطوير وستُعرض الصفحة الخاصة بهذه المرحلة بدلًا منه.


== التوليد الساكن للمسارات باستخدام الدالة getStaticProps ==
== التوليد الساكن للمسارات باستخدام الدالة <code>getStaticPaths</code> ==
عندما تمتلك الصفحة مسارات ديناميكية وتستخدم في الوقت نفسه الدالة <code>getStaticProps</code>، لا بد حينها من تعريف قائمة بالمسارات التي ينبغي توليدها بشكل ساكن. مع تصدير الدالة التي تُدعى <code>getStaticPaths</code> من <syntaxhighlight>
عندما تمتلك الصفحة [[Next.js/Routing#.D8.A7.D9.84.D9.88.D8.AC.D9.87.D8.A7.D8.AA .D8.A7.D9.84.D8.AF.D9.8A.D9.86.D8.A7.D9.85.D9.8A.D9.83.D9.8A.D8.A9 .D9.81.D9.8A Next.js|مسارات ديناميكية]] وتستخدم في الوقت نفسه الدالة <code>getStaticProps</code>، لا بد حينها من تعريف قائمة بالمسارات التي ينبغي توليدها بشكل ساكن، وعند تصدير الدالة التي تُدعى <code>getStaticPaths</code> (في آلية توليد الساكن للشيفرة) من صفحة تعتمد على مسارات ديناميكية، فستعمل Next.js آلية التصيير المسبق pre-render لتصيير جميع المسارات المعرفة عبر هذه الدالة. <syntaxhighlight lang="javascript">
// pages/posts/[id].js
 
// ‫يولد المسار `/posts/1` والمسار `/posts/2`
export async function getStaticPaths() {
export async function getStaticPaths() {
   return {
   return {
     paths: [
     paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
      { params: { ... } }
     fallback: false, // can also be true or 'blocking'
    ],
   }
     fallback: true // false or 'blocking'
}
   };
 
// ‫`getStaticPaths` يتطلب استعمال الدالة `getStaticProps`
export async function getStaticProps(context) {
  return {
    // Passed to the page component as props
    props: { post: {} },
  }
}
 
export default function Post({ post }) {
  // Render post...
}
}
</syntaxhighlight>ينبغي استخدام الدالة <code>getStaticPaths</code> مع الدالة <code>getStaticProps</code>، ولا يمكنك استخدامها مع <code>getServerSideProps</code>.


يمكنك معرفة جميع المعاملات والخاصيات التي تُستخدم مع <code>getStaticPaths</code>، بالاطلاع على [[Next.js/api data fetching|مرجع الواجهة البرمجية]] API reference.
</syntaxhighlight>يمكنك معرفة جميع المعاملات والخاصيات التي تُستخدم مع <code>getStaticPaths</code>، بالاطلاع على [[Next.js/api data fetching|مرجع الواجهة البرمجية]] API reference.


=== حالات استخدام الدالة getStaticPaths في Next.js ===
=== حالات استخدام الدالة <code>getStaticPaths</code> ===
ينبغي استخدام هذه الدالة إن كنت ستصيّر مسبقًا صفحات تحتوي على مسارات ديناميكية وكان:
ينبغي استخدام هذه الدالة إن كنت ستصيّر مسبقًا صفحات تحتوي على مسارات ديناميكية وكان:


* مصدر البيانات هو منظومة إدارة محتوى دون ترويسات.
* مصدر البيانات هو منظومة إدارة محتوى دون واجهة headless CMS.
* مصدر البيانات هو قاعدة بيانات.
* مصدر البيانات هو قاعدة بيانات.
* مصدر البيانات هو نظام إدارة ملفات.
* مصدر البيانات هو نظام إدارة ملفات.
سطر 104: سطر 222:
* لا بدّ من تصيير الصفحة مسبقًا (لأغراض تحسين محركات البحث "سيو") وأن يجري ذلك بسرعة. تولّد <code>getStaticProps</code> ملفات <code>HTML</code> و <code>JSON</code> التي يمكن تخزينها مؤقتًا من قبل منظومة توصيل محتوى CDN لرفع الأداء.
* لا بدّ من تصيير الصفحة مسبقًا (لأغراض تحسين محركات البحث "سيو") وأن يجري ذلك بسرعة. تولّد <code>getStaticProps</code> ملفات <code>HTML</code> و <code>JSON</code> التي يمكن تخزينها مؤقتًا من قبل منظومة توصيل محتوى CDN لرفع الأداء.


=== تنفيذ الدالة getStaticPaths في Next.js ===
=== تنفيذ الدالة <code>getStaticPaths</code> ===
تُنفَّذ هذه الدالة عند بناء التطبيق في وضع الإنتاج فقط. يمكنك التحقق من أن الشيفرة المكتوبة داخل الدالة <code>getStaticPaths</code> ستُزال من الشيفرة المجمّعة من ناحية العميل باستخدام الأداة [https://next-code-elimination.vercel.app/ next-code-elimination tool].
تُنفَّذ هذه الدالة عند بناء التطبيق في وضع الإنتاج فقط. يمكنك التحقق من أن الشيفرة المكتوبة داخل الدالة <code>getStaticPaths</code> ستُزال من الشيفرة المجمّعة من ناحية العميل باستخدام الأداة [https://next-code-elimination.vercel.app/ next-code-elimination tool].
تنفيذ الدالة <code>getStaticProps</code> يتعلق بتنفيذ الدالة <code>getStaticPaths</code> وفق ما يلي:


* تُنفَّذ الدالة <code>getStaticProps</code> عند تنفيذ الأمر <code>next build</code> لكل مسار <code>paths</code> يُعاد أثناء البناء.
* تُنفَّذ الدالة <code>getStaticProps</code> عند تنفيذ الأمر <code>next build</code> لكل مسار <code>paths</code> يُعاد أثناء البناء.
سطر 111: سطر 231:
* تُستدعى الدالة <code>getStaticProps</code> قبل التصيير الأولي عند استخدام التوجيه <code>fallback: blocking</code>.
* تُستدعى الدالة <code>getStaticProps</code> قبل التصيير الأولي عند استخدام التوجيه <code>fallback: blocking</code>.


'''ملاحظة:''' تُستدعى الدالة عند كل استدعاء خلال مرحلة التطوير (<code>next dev</code>).
=== أماكن استخدام الدالة <code>getStaticPaths</code> ===
يمكن استخدام الدالة <code>getStaticPaths</code> في المواضع التالية:
 
* تُستخدم مع الدالة .
* لا يمكن استخدامها مع الدالة .
* يمكن تصديرها من صفحة ذات مسارات ديناميكية تستخدم أيضًا الدالة .
* لا يمكن تصديرها من ملف غير موجود ضمن مجلد الصفحات pages (مثلًا من مجلد المكونات components).
* يجب تصديرها كدالة قائمة بحد ذاتها standalone، فلن تعمل إن أضفتها كخاصية من خواص مكوّن الصفحة.'''ملاحظة:''' تُستدعى الدالة عند كل استدعاء خلال مرحلة التطوير (<code>next dev</code>).
 
=== توليد المسارات عند الطلب ===
تتيح الدالة <code>getStaticPaths</code> إمكانية التحكم بأي صفحة تُولَّد أثناء عملية البناء بدلًا من توليدها عند الطلب on-demand مع الخيار <code>fallback</code>، إذ سيودي توليد الكثير من الصفحات إلى حدوث بطء في عملية البناء.
 
يمكنك تأجيل عملية توليد كل الصفحات عند الطلب بإعادة مصفوفة فارغة مع <code>paths</code>، وتبرز فائدة ذلك عند نشر تطبيق Next.js على أكثر من بيئة، إذ تصبح عملية البناء سريعة آنذاك في حال كنا نريد ذلك مثل تجربة التطبيق (أثناء التطوير وليس في بيئة الإنتاج)، وعلى أي حال هذا مفيد أيضًا في المواقع التي تتألف من مئات أو آلاف الصفحات الساكنة.<syntaxhighlight lang="javascript">
// pages/posts/[id].js
 
export async function getStaticPaths() {
  // في حال كنا نريد تجربة التطبيق في وضع الاستعراض، فلا يجري تصيير أي
  // صفحة ساكنة تصييرًا مسبقًا. وهذا يُسرع عملية البناء
  // ولكن تصبح عملية تحميل الصفحة الابتدائية بطيئة
  if (process.env.SKIP_BUILD_STATIC_GENERATION) {
    return {
      paths: [],
      fallback: 'blocking',
    }
  }
 
  // جلب جميع المنشورات عبر وصلة خارجية
  const res = await fetch('https://.../posts')
  const posts = await res.json()
 
  // جلب جميع المسارات التي نريد تصييرها مسبقًا لما يقابل كل منشور
  // وصير كل الصفحات مسبقًا في بيئة الإنتاج، وهذا يُبطئ عملية البناء
  // ولكن يسرع من عملية تحميل الصفحة الابتدائية
  const paths = posts.map((post) => ({
    params: { id: post.id },
  }))
 
  // يعني أن بقية المسارات الأخرى تتوجه إلى الصفحة 404 { fallback: false }
  return { paths, fallback: false }
}
 
</syntaxhighlight>
 
== التوليد الساكن للصفحات باستخدام الدالة <code>getStaticProps</code> ==


== التوليد الساكن للصفحات في Next.js باستخدام الدالة getStaticProps ==
عندما تُصدِّر دالة تُدعى <code>getStaticProps</code> (توليد ساكن للموقع) من صفحة، تُصيِّر Next.js هذه الصفحة أثناء بناء التطبيق باستخدام الخاصيات props التي تُعيدها هذه الدالة.<syntaxhighlight lang="javascript">
عندما تُصدِّر دالة تُدعى <code>getStaticProps</code> (توليد ساكن للموقع) من صفحة، تُصيِّر Next.js هذه الصفحة أثناء بناء التطبيق باستخدام الخاصيات props التي تُعيدها هذه الدالة.<syntaxhighlight lang="javascript">
export async function getStaticProps(context) {
export async function getStaticProps(context) {
سطر 120: سطر 282:
   }
   }
}
}
</syntaxhighlight>
</syntaxhighlight>'''ملاحظة''': بغض النظر عن نوع التصيير، أي شيء يُمرر إلى مكون الصفحة page ضمن الخاصيات props يُعرَض في طرف العميل ضمن صفحة HTML، وصحيح هذا يساعد على ترطيب الصفحة وتغذيها بالبيانات والمعلومات، ولكن كن حذرًا من تمرير أي بيانات حساسة لا يجب أن تُمرر إلى طرف العميل.


=== حالات استخدام الدالة getStaticProps في Next.js ===
=== حالات استخدام الدالة <code>getStaticProps</code> ===
عليك استخدام هذه الدالة في الحالات التالية:
عليك استخدام هذه الدالة في الحالات التالية:


* البيانات التي ستستخدمها لتصيير الصفحة أثناء بناء التطبيق متوفرة مسبقًا قبل أن يطلبها المستخدم.
* البيانات التي ستستخدمها لتصيير الصفحة أثناء بناء التطبيق متوفرة مسبقًا قبل أن يطلبها المستخدم.
* مصدر البيانات هو نظام إدارة محتوى دون ترويسات headless CMS.
* مصدر البيانات هو نظام إدارة محتوى دون واجهة headless CMS.
* لا بدّ من تصيير الصفحة مسبقًا (لأغراض تحسين محركات البحث "سيو") وأن يجري ذلك بسرعة. تولّد <code>getStaticProps</code> ملفات <code>HTML</code> و <code>JSON</code> التي يمكن تخزينها مؤقتًا من قبل منظومة توصيل محتوى CDN لرفع الأداء.
* لا بدّ من تصيير الصفحة مسبقًا (لأغراض تحسين محركات البحث "سيو") وأن يجري ذلك بسرعة. تولّد <code>getStaticProps</code> ملفات <code>HTML</code> و <code>JSON</code> التي يمكن تخزينها مؤقتًا من قبل منظومة توصيل محتوى CDN لرفع الأداء.
* عند إمكانية التخزين المؤقت للبيانات للعموم (غير مخصصة لمستخدم محدد). يمكن تجاوز هذا الشرط في بعض الحالات الخاصة من خلال استخدام برمجيات وسطية Middleware لإعادة كتابة المسار.
* عند إمكانية التخزين المؤقت للبيانات للعموم (غير مخصصة لمستخدم محدد). يمكن تجاوز هذا الشرط في بعض الحالات الخاصة من خلال استخدام برمجيات وسطية Middleware لإعادة كتابة المسار.


=== تنفيذ الدالة getStaticProps في Next.js ===
=== تنفيذ الدالة getStaticProps ===
تُنفَّذ هذه الدالة دائمًا من جانب الخادم وليس من جانب العميل:
تُنفَّذ هذه الدالة دائمًا من جانب الخادم وليس من جانب العميل ويمكنك التحقق من أن الشيفرة المكتوبة داخل الدالة <code>getStaticProps</code> ستُزال من الشيفرة المجمّعة من ناحية العميل باستخدام الأداة [https://next-code-elimination.vercel.app/ next-code-elimination tool]:


* تُنفَّذ <code>getStaticProps</code> دائمًا أثناء تنفيذ الأمر
* تُنفَّذ <code>getStaticProps</code> دائمًا أثناء تنفيذ الأمر <code>next build</code>.
* تُنفّذ <code>getStaticProps</code> في الخلفية عند استخدام التوجيه <code>revalidate</code>.
* تُنفّذ <code>getStaticProps</code> في الخلفية عند استخدام التوجيه <code>revalidate</code>.
* تُنفّذ <code>getStaticProps</code> في الخلفية عند الطلب عند استخدام التوجيه <code>unstable_revalidate</code>.
* تُنفّذ <code>getStaticProps</code> في الخلفية عند الطلب عند استخدام <code>revalidate()‎</code>.


عندما تُستخدم الدالة <code>getStaticProps</code> مع نمط التجديد الساكن التدريجي للصفحات Incremental Static Regeneration، ستُنفَّذ في الخلفية أثناء إعادة تقييم الصفحة القديمة ومن ثم تُقدّم الصفحة المحدّثة إلى المتصفح.
عندما تُستخدم الدالة <code>getStaticProps</code> مع نمط التجديد الساكن التدريجي للصفحات Incremental Static Regeneration، ستُنفَّذ في الخلفية أثناء إعادة تقييم الصفحة القديمة ومن ثم تُقدّم الصفحة المحدّثة إلى المتصفح.


لا يمكن للدالة <code>getStaticProps</code> أن تلج إلى الطلبات الواردة (كمعاملات الاستعلام أو ترويسات HTTP) لأنها تولّد ملف HTML ساكن. لكن إن أردت الوصول إلى هذه الطلبات فكِّر باستخدام برمجيات وسطية إضافة إلى هذه الدالة.
لا يمكن للدالة <code>getStaticProps</code> أن تلج إلى الطلبات الواردة (كمعاملات الاستعلام أو ترويسات HTTP) لأنها تولّد ملف HTML ساكن، لكن إن أردت الوصول إلى هذه الطلبات فكِّر باستخدام برمجيات وسطية إضافة إلى هذه الدالة.


=== استخدام الدالة لإحضار البيانات من منظومة إدارة محتوى CMS ===
=== استخدام الدالة لإحضار البيانات من منظومة إدارة محتوى CMS ===
سطر 162: سطر 324:
   const posts = await res.json()
   const posts = await res.json()


   // المنشورات كخاصيات أثناء بناء التطبيقBlog يتلقى المكوّن  
   // المنشورات كخاصيات أثناء بناء التطبيق Blog يتلقى المكوّن  
   // { props: { posts } } بإعادة
   // { props: { posts } } بإعادة
   return {
   return {
سطر 174: سطر 336:
</syntaxhighlight>يمكنك معرفة جميع المعاملات والخاصيات التي تُستخدم مع <code>getStaticProps</code>، بالاطلاع على [[Next.js/api data fetching|مرجع الواجهة البرمجية]] API reference.
</syntaxhighlight>يمكنك معرفة جميع المعاملات والخاصيات التي تُستخدم مع <code>getStaticProps</code>، بالاطلاع على [[Next.js/api data fetching|مرجع الواجهة البرمجية]] API reference.


=== كتابة شيفرة تعمل من جانب الخادم مباشرة في Next.js ===
=== كتابة شيفرة تعمل من جانب الخادم مباشرة ===
لن تُنفَّذ الدالة <code>getStaticProps</code> إلا من ناحية الخادم فقط ولا يمكن تنفيذها من ناحية العميل وبالتالي لن تجدها ضمن شيفرة جافا سكربت الخاصة بالمتصفح وستتمكن من كتابة استعلامات مباشرة من قاعدة البيانات دون إرسالها إلى المتصفحات. وهكذا يمكنك كتابة شيفرة تُنفَّذ من جانب الخادم مباشرة ضمن الدالة <code>getStaticProps</code> بدلًا من إحضار مسار API منها (كونها تحضر البيانات بنفسها من مصدر خارجي).  
لن تُنفَّذ الدالة <code>getStaticProps</code> إلا من ناحية الخادم فقط ولا يمكن تنفيذها من ناحية العميل وبالتالي لن تجدها ضمن شيفرة جافا سكربت الخاصة بالمتصفح وستتمكن من كتابة استعلامات مباشرة من قاعدة البيانات دون إرسالها إلى المتصفحات. وهكذا يمكنك كتابة شيفرة تُنفَّذ من جانب الخادم مباشرة ضمن الدالة <code>getStaticProps</code> بدلًا من كتابة مسار واجهة API أي API route ليحضر تلك البيانات من واجهة خارجية عند استدعائها.  


لنلق نظرة على المثال التالي: يُستخدم مسار API لإحضار بيانات من منظومة إدارة محتوى، لهذا سيُستدعى مباشرة من خلال الدالة <code>getStaticProps</code>، وستكون النتيجة استدعاء إضافي وانخفاضًا في الأداء. وبدلًا عن ذلك، يمكن مشاركة منطق إحضار البيانات من منظومة إدارة المحتوى باستخدام المجلد <code>lib/</code> ومن ثم مشاركته مع الدالة <code>getStaticProps</code>. <syntaxhighlight lang="javascript">
لنلق نظرة على المثال التالي: يُستخدم مسار API لإحضار بيانات من منظومة إدارة محتوى، لهذا سيُستدعى مباشرة من خلال الدالة <code>getStaticProps</code>، وستكون النتيجة استدعاء إضافي وانخفاضًا في الأداء. وبدلًا عن ذلك، يمكن استيراد منطق إحضار البيانات من منظومة إدارة المحتوى من المجلد <code>lib/</code> مثلًا ومن ثم استدعائه مباشرة ضمن الدالة <code>getStaticProps</code>. <syntaxhighlight lang="javascript">
// lib/fetch-posts.js
// lib/fetch-posts.js
// تُشارك الدالة التالية
// تُشارك الدالة التالية
سطر 183: سطر 345:
// `lib/` من المجلد
// `lib/` من المجلد
export async function loadPosts() {
export async function loadPosts() {
   //لخارجية للحصول على المنشوراتAPI تستدعى وصلة  
   //لخارجية للحصول على المنشورات API تستدعى وصلة  
   const res = await fetch('https://.../posts/')
   const res = await fetch('https://.../posts/')
   const data = await res.json()
   const data = await res.json()
سطر 202: سطر 364:
}
}
</syntaxhighlight>إن لم تستخدم مسارات API لإحضار البيانات بإمكانك استخدام الدالة <code>[https://academy.hsoub.com/programming/javascript/%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84%D8%A7%D8%AA-%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9-%D9%84%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-fetch-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1297/ ()fetch]</code> ضمن الدالة <code>getStaticProps</code>.
</syntaxhighlight>إن لم تستخدم مسارات API لإحضار البيانات بإمكانك استخدام الدالة <code>[https://academy.hsoub.com/programming/javascript/%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84%D8%A7%D8%AA-%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9-%D9%84%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-fetch-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1297/ ()fetch]</code> ضمن الدالة <code>getStaticProps</code>.
يمكنك التحقق مما سيُزال من الشيفرة المجمّعة من ناحية العميل باستخدام الأداة [https://next-code-elimination.vercel.app/ next-code-elimination tool].


=== التوليد التلقائي لشيفرة HTML و JSON ===
=== التوليد التلقائي لشيفرة HTML و JSON ===
عندما تُصيَّر صفحة تحتوي على الدالة <code>getStaticProps</code> أثناء بناء التطبيق، ستولّد Next.js ملف JSON بالإضافة إلى ملف HTML وذلك لاحتواء نتيجة تنفيذ تلك الدالة.
عندما تُصيَّر صفحة تحتوي على الدالة <code>getStaticProps</code> أثناء بناء التطبيق، ستولّد Next.js ملف JSON بالإضافة إلى ملف HTML وذلك لاحتواء نتيجة تنفيذ تلك الدالة.


يُستخدم ملف JSON للتوجيه من جانب العميل عبر <code>next/link</code> أو <code>next/router</code>. فعندما ننتقل إلى الصفحة التي صُيِّرت مسبقًا باستخدام الدالة <code>getStaticProps</code>، ستحضر Next.js ملف JSON (الذي بني مُسبقًا أثناء بناء التطبيق) ثم تسنده إلى خصائص مكوِّن الصفحة. وبالتالي لن تستدعي عمليات التنقل بين صفحات التطبيق من ناحية العميل استدعاء الدالة <code>getStaticProps</code> لأن ما سيُستخدم فعلًا هو ملف JSON المُصدَّر.
يُستخدم ملف JSON ذاك للتوجيه من جانب العميل عبر <code>next/link</code> أو <code>next/router</code>. فعندما ننتقل إلى الصفحة التي صُيِّرت مسبقًا باستخدام الدالة <code>getStaticProps</code>، ستحضر Next.js ملف JSON (الذي بني مُسبقًا أثناء بناء التطبيق) ثم تسنده إلى خصائص مكوِّن الصفحة. وبالتالي لن تستدعي عمليات التنقل بين صفحات التطبيق من ناحية العميل استدعاء الدالة <code>getStaticProps</code> لأن ما سيُستخدم فعلًا هو ملف JSON المُصدَّر.


عند استخدام التوليد الساكن التدريجي أو التراكمي، سيجر تنفيذ الدالة <code>getStaticProps</code> في الخلفية لتوليد ملف JSON المطلوب للتنقل بين الصفحات من جانب العميل. قد ترى ذلك على شكل طلبات متعدددة لنفس الصفحة، وهذا أمر مقصود ولن يؤثر على أداء الصفحة بالنسبة للمستخدم النهائي.
عند استخدام التوليد الساكن التدريجي أو التراكمي، سيجري تنفيذ الدالة <code>getStaticProps</code> في الخلفية لتوليد ملف JSON المطلوب للتنقل بين الصفحات من جانب العميل. قد ترى ذلك على شكل طلبات متعدددة لنفس الصفحة، وهذا أمر مقصود ولن يؤثر على أداء الصفحة بالنسبة للمستخدم النهائي.


=== أماكن استخدام الدالة getStaticProps في Next.js ===
=== أماكن استخدام الدالة getStaticProps ===
يمكن تصدير الدالة <code>getStaticProps</code> من صفحة Next.js (مكوّن صفحة) فقط ولا يمكن تصديرها من ملفات لا تمثل صفحات. ومن أهم الأسباب الكامنة وراء هذا التقييد هو أن رياكت تحتاج إلى جميع البيانات قبل تصيير الصفحة.
يمكن تصدير الدالة <code>getStaticProps</code> من صفحة Next.js (مكوّن صفحة) فقط ولا يمكن تصديرها من ملفات لا تمثل صفحات. ومن أهم الأسباب الكامنة وراء هذا التقييد هو أن [[React|رياكت]] تحتاج إلى جميع البيانات قبل تصيير الصفحة.


لا بد أن تُصدّر أيضًا الدالة <code>getStaticProps</code> لتكون ذاتية الدعم فلن تعمل إن أضفتها كخاصية إلى مكوِّن الصفحة.
لا بد أن تُصدّر أيضًا الدالة <code>getStaticProps</code> لتكون دالة قائمة بذاتها، فلن تعمل إن أضفتها كخاصية إلى مكوِّن الصفحة.


'''ملاحظة:''' تُستدعى الدالة عند كل استدعاء خلال مرحلة التطوير (<code>next dev</code>).
'''ملاحظة:''' إن أنشأت [[Next.js/custom app|تطبيقًا مخصصًا]] فتأكد من تمرير <code>pageProps</code> إلى مكون الصفحة وإلا ستكون الخاصيات props فارغة.
 
تُستدعى الدالة عند كل استدعاء خلال مرحلة التطوير (<code>next dev</code>).


=== نمط الاستعراض ===
=== نمط الاستعراض ===
بإمكانك أن تتجاوز التوليد التلقائي مؤقتًا وتصيِّر الصفحة عند الطلب بدلًا من تصييرها أثناء بناء التطبيق باستخدام [[Next.js/preview mode|نمط الاستعراض]] Preview Mode. فقد تستخدم مثلًا منظومة إدارة محتوى بلا ترويسات، وتريد أن تستعرض مسودة العمل قبل نشره.
بإمكانك أن تتجاوز التوليد التلقائي مؤقتًا وتصيِّر الصفحة عند الطلب بدلًا من تصييرها أثناء بناء التطبيق باستخدام [[Next.js/preview mode|نمط الاستعراض]] Preview Mode. فقد تستخدم مثلًا منظومة إدارة محتوى بلا واجهة، وتريد أن تستعرض مسودة للموقع قبل نشره.


== التجديد التدريجي الساكن في Next.js ==
== التجديد التدريجي الساكن ==
تتيح لك أن تنشئ أو تُحدِّث الصفحات الساكنة بعد ان تنهي بناء الموقع من خلال التجديد التدريجي الساكن Incremental Static Regeneration (واختصارًا ISR) الذي يمكِّنك من استخدام التجديد الساكن للصفحات منفردةً دون الحاجة إلى إعادة بناء الموقع بأكمله. وهكذا ستحتفظ بميزات الصفحات الساكنة مع إتوسيع إمكانات ملايين الصفحات.
تتيح لك Next.js أن تنشئ أو تُحدِّث الصفحات الساكنة بعد أن تنهي بناء الموقع من خلال التجديد التدريجي الساكن Incremental Static Regeneration (واختصارًا ISR) الذي يمكِّنك من استخدام التجديد الساكن للصفحات منفردةً دون الحاجة إلى إعادة بناء الموقع بأكمله، وهكذا ستحتفظ بميزات الصفحات الساكنة مع توسيع إمكانات ملايين الصفحات.


لاستخدام ISR أضف الخاصية <code>revalidate</code> إلى الدالة <code>getStaticProps</code>:<syntaxhighlight lang="javascript">
لاستخدام ISR أضف الخاصية <code>revalidate</code> إلى الدالة <code>getStaticProps</code>:<syntaxhighlight lang="javascript">
سطر 234: سطر 400:
}
}


// تُستدعى هذه الدالة اثناء البناء من جانب الخادم
// تُستدعى هذه الدالة أثناء البناء من جانب الخادم
// يمكن استدعاؤها مجددًا من قبل دالة لا تعمل علة الخادم  
// يمكن استدعاؤها مجددًا من قبل دالة لا تعمل على الخادم  
// عندما تُفعّل خاصية إعادة التحقق ويصل طلب جديد
// عندما تُفعّل خاصية إعادة التحقق ويصل طلب جديد
export async function getStaticProps() {
export async function getStaticProps() {
سطر 252: سطر 418:
}
}


// تُستدعى هذه الدالة اثناء البناء من جانب الخادم
// تُستدعى هذه الدالة أثناء البناء من جانب الخادم
// يمكن استدعاؤها مجددًا من قبل دالة لا تعمل علة الخادم  
// يمكن استدعاؤها مجددًا من قبل دالة لا تعمل على الخادم  
// إن لم يُحدّث المسار
// إن لم يُحدّث المسار
export async function getStaticPaths() {
export async function getStaticPaths() {
سطر 259: سطر 425:
   const posts = await res.json()
   const posts = await res.json()


   // الحصول على المسارات التي نريد غعادة تصييرها وفقًا للمنشورات المطلوبة
   // الحصول على المسارات التي نريد إعادة تصييرها وفقًا للمنشورات المطلوبة
   const paths = posts.map((post) => ({
   const paths = posts.map((post) => ({
     params: { id: post.id },
     params: { id: post.id },
سطر 273: سطر 439:
</syntaxhighlight>عندما تُطلب صفحة مصيّرة مسبقًا أثناء بناء التطبيق فستعرض مبدئيًا البيانات الموجودة في الذاكرة المؤقتة:
</syntaxhighlight>عندما تُطلب صفحة مصيّرة مسبقًا أثناء بناء التطبيق فستعرض مبدئيًا البيانات الموجودة في الذاكرة المؤقتة:


* عندما تُطلب الصفحة مجددًا ضمن نافذة الثواني العشرة بعد الطلب الأساسي ستُعرض الصفحة ذاتها مباشرة من بيانات الذاكرة المؤقتة.
* عندما تُطلب الصفحة مجددًا ضمن مدة الثواني العشرة بعد الطلب الأساسي ستُعرض الصفحة ذاتها مباشرة من بيانات الذاكرة المؤقتة.
* بعد الثواني العشرة أيضًا ستُعرض الصفحة المخزنة نفسها أيضًا (الصفحة القديمة).
* بعد الثواني العشرة أيضًا ستُعرض الصفحة المخزنة نفسها أيضًا (الصفحة القديمة).
* لكن ما يحدث حينها أن Next.js ستتطلب تجديد الصفحة في الخلفية.
* لكن ما يحدث حينها أنّ Next.js ستطلب تجديد الصفحة في الخلفية.
* عندما تجدد الصفحة بنجاح، تتحقق من بيانات الذاكرة المؤقتة للصفحة وتحدث ما يجب تحديثه، وستبقى الصفحة القديمة نفسها إن فشل التجديد.
* عندما تجدد الصفحة بنجاح، تتحقق من بيانات الذاكرة المؤقتة للصفحة وتحدث ما يجب تحديثه، وستبقى الصفحة القديمة نفسها إن فشل التجديد.


عندما يُطلب مسار محدد لم يُحدّث بعد، ستصيِّر الصفحة من جانب الخادم عند أول طلب، بينما ستُخدَّم الصفحة الساكنة عند ورود أية طلبات جديدة من الذاكرة المؤقتة.  
عندما يُطلب مسار محدد لم يُحدّث بعد، ستصيِّر الصفحة من جانب الخادم عند أول طلب، بينما ستُخدَّم الصفحة الساكنة عند ورود أية طلبات جديدة من الذاكرة المؤقتة.  


=== إعادة التحقق حين الطلب (ميزة تجريبية "بيتا") ===
=== إعادة التحقق حين الطلب ===
عندما تضبط قيمة الخاصية <code>revalidate</code> على 60 ثانية، سيرى كل زوار صفحتك النسخة الموّلدة عن موقعك مدة دقيقة كاملة، والطريقة الوحيدة للتأكد من صحة بيانات الذاكرة المؤقتة هو زيارة الصفحة بعد مضي دقيقة.  
عندما تضبط قيمة الخاصية <code>revalidate</code> على 60 ثانية، سيرى كل زوار صفحتك النسخة الموّلدة عن موقعك مدة دقيقة كاملة، والطريقة الوحيدة للتأكد من صحة بيانات الذاكرة المؤقتة هو زيارة الصفحة بعد مضي دقيقة.  


تدعم Next.js بدءًا من النسخة <code>v12.1.0</code> التجديد التدريجي للصفحات حين الطلب، وذلك للتحقق اليدوي من بيانات الذاكرة المؤقتة الخاصة بهذه الصفحة. سيسهّل ذلك تحديث الموقع عندما:
تدعم Next.js بدءًا من النسخة <code>v12.2.0</code> التجديد التدريجي للصفحات حين الطلب، وذلك للتحقق اليدوي من بيانات الذاكرة المؤقتة الخاصة بهذه الصفحة. سيسهّل ذلك تحديث الموقع عندما:


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


لا حاجة للخاصية <code>revalidate</code> داخل الدالة <code>getStaticProps</code> لاستخدام ميزة إعادة التحقق حين الطلب. فإن لم تحدد قيمة الخاصية <code>revalidate</code> في شيفرتك، تستخدم Next.js القيمة الافتراضية وهي <code>false</code> (لا إعادة للتحقق) وبالتالي لن يُعاد التحقق من بيانات الصفحة المخزنة إلا حين الطلب وذلك باستدعاء الدالة <code>unstable_revalidate</code>.
لا حاجة للخاصية <code>revalidate</code> داخل الدالة <code>getStaticProps</code> لاستخدام ميزة إعادة التحقق حين الطلب. فإن لم تحدد قيمة الخاصية <code>revalidate</code> في شيفرتك، تستخدم Next.js القيمة الافتراضية وهي <code>false</code> (لا إعادة للتحقق) وبالتالي لن يُعاد التحقق من بيانات الصفحة المخزنة إلا حين الطلب وذلك باستدعاء الدالة <code>revalidate()‎</code>.


==== استخدام إعادة التحقق عند الطلب ====
==== استخدام إعادة التحقق عند الطلب ====
سطر 303: سطر 469:


   try {
   try {
     await res.unstable_revalidate('/path-to-revalidate')
     await res.revalidate('/path-to-revalidate')
     return res.json({ revalidated: true })
     return res.json({ revalidated: true })
   } catch (err) {
   } catch (err) {
سطر 311: سطر 477:
   }
   }
}
}
</syntaxhighlight>
</syntaxhighlight>انظر [https://on-demand-isr.vercel.app/ هذا المثال].


==== اختبار التجديد التدريجي الساكن ISR حين الطلب في نمط التطوير ====
==== اختبار التجديد التدريجي الساكن ISR حين الطلب في نمط التطوير ====
عندما تشغل التطبيق محليًا من خلال الأمر ، تُستدعى الدالة مع كل طلب. وللتحقق من صحة إعدادات ISR حين الطلب، لا بد من إنشاء نسخة إنتاج وتشغيل خادم الإنتاج:<syntaxhighlight lang="bash">
عندما تشغل التطبيق محليًا من خلال الأمر <code>next dev</code>، تُستدعى الدالة مع كل طلب. وللتحقق من صحة إعدادات ISR حين الطلب، لا بد من إنشاء نسخة إنتاج وتشغيل خادم الإنتاج:<syntaxhighlight lang="bash">
$ next build
$ next build
$ next start
$ next start
سطر 320: سطر 486:


=== معالجة الأخطاء وإعادة التحقق ===
=== معالجة الأخطاء وإعادة التحقق ===
إن حصل خطأ ما داخل الدالة <code>getStaticProps</code> عندما تجدد البيانات في الخلفية أو عندما توقف التنفيذ من خلال الشيفرة يدويًا (في حال التقطت خطأً)، فإن ما يُعرض هو آخر توليد ناجح للصفحة. ستحاول Next.js خلال الطلبات الفرعية اللاحقة استدعاء الدالة <code>getStaticProps</code>:<syntaxhighlight lang="javascript">
إن حصل خطأ ما داخل الدالة <code>getStaticProps</code> عندما تُجدَّد البيانات في الخلفية أو عندما توقف التنفيذ من خلال الشيفرة يدويًا (في حال التقطت خطأً)، فإن ما يُعرض هو آخر توليد ناجح للصفحة. ستحاول Next.js خلال الطلبات الفرعية اللاحقة استدعاء الدالة <code>getStaticProps</code>:<syntaxhighlight lang="javascript">
export async function getStaticProps() {
export async function getStaticProps() {
   //من بيانات الصفحة Next.js إن تسبب الطلب في وقوع خطأ غير متوقع، لن تتحقق  
   //من بيانات الصفحة Next.js إن تسبب الطلب في وقوع خطأ غير متوقع، لن تتحقق  
   //عند الطلب التالي getStaticProps وستحاول ايتدعاء الدالة  
   //عند الطلب التالي getStaticProps وستحاول استدعاء الدالة  
   const res = await fetch('https://.../posts')
   const res = await fetch('https://.../posts')
   const posts = await res.json()
   const posts = await res.json()
سطر 347: سطر 513:


=== الاستضافة الذاتية والتجديد التدريجي الساكن ISR ===
=== الاستضافة الذاتية والتجديد التدريجي الساكن ISR ===
يعمل التجديد التدريجي الساكن في مواقع Next.js التي تريد أن [[Next.js/deployment|تديرها بنفسك]] بطريقة مختلفة عندما تستخدم <code>next start</code>. وبإمكانك استخدام هذا الأسلوب عندما تنشر الموقع على منسق حاويات container orchestrator مثل [https://kubernetes.io Kubernetes] أو [https://www.nomadproject.io HashiCorp Nomad]. تُخزّن الموجودات افتراضيًا في الذاكرة ضمن كل علبة pod، ويعني ذلك وجود نسخة عن كل ملف ساكن في كل علبة. ستُعرض البيانات القديمة حتى تُستهدف علبة محددة بطلب.
يعمل التجديد التدريجي الساكن في مواقع Next.js التي تريد أن [[Next.js/deployment|تديرها بنفسك]] بطريقة مختلفة عندما تستخدم <code>next start</code>. وبإمكانك استخدام هذا الأسلوب عندما تنشر الموقع على منسق حاويات container orchestrator مثل [https://kubernetes.io Kubernetes] أو [https://www.nomadproject.io HashiCorp Nomad]. تُخزّن الأصول assets افتراضيًا في الذاكرة ضمن كل علبة pod، ويعني ذلك وجود نسخة عن كل ملف ساكن في كل علبة. ستُعرض البيانات القديمة حتى تُستهدف علبة محددة بطلب.


بإمكانك تعطيل ميزة التخزين المؤقت في الذاكرة لضمان الترابط بين جميع العلب، مما يدفع الخادم إلى الاستفادة من الموجودات التي ولّدها ISR ضمن نظام المفات. كما يمكنك مشاركة مخزن على الشبكة network mount ضمن علب منسق الحاويات Kubernetes (أو أية ترتيب مشابه) لإعادة استخدام الذاكرة المؤقتة لمنظومة الملفات بين مختلف الحاويات. وبهذا الأسلوب سيُشارك المجلد <code>next.</code> والذي يحتوي على المخزن المؤقت <code>next/image</code> ويعاد استخدامه.
بإمكانك تعطيل ميزة التخزين المؤقت في الذاكرة لضمان الترابط بين جميع العلب، مما يدفع الخادم إلى الاستفادة من الموجودات التي ولّدها ISR ضمن نظام الملفات. كما يمكنك مشاركة مخزن على الشبكة network mount ضمن علب منسق الحاويات Kubernetes (أو أية ترتيب مشابه) لإعادة استخدام الذاكرة المؤقتة لمنظومة الملفات بين مختلف الحاويات. وبهذا الأسلوب سيُشارك المجلد <code>next.</code> والذي يحتوي على المخزن المؤقت <code>next/image</code> ويعاد استخدامه.


ولتعطيل ذاكرة التخزين المؤقت، اضبط قيمة الخاصية <code>isrMemoryCacheSize</code> على القيمة <code>0</code> ضمن الملف <code>next.config.js</code> :<syntaxhighlight lang="javascript">
ولتعطيل ذاكرة التخزين المؤقت، اضبط قيمة الخاصية <code>isrMemoryCacheSize</code> على القيمة <code>0</code> ضمن الملف <code>next.config.js</code>:<syntaxhighlight lang="javascript">
module.exports = {
module.exports = {
   experimental: {
   experimental: {
سطر 358: سطر 524:
   },
   },
}
}
</syntaxhighlight><blockquote>'''ملاحظة''': لا بد أن تاخذ في الحسبان السباق بين العلب المختلفة التي تحاول تحديث الذاكرة المؤقتة في الوقت ذاته، وذلك وفقًا لطريقة تهيئة المخزن المشترك.</blockquote>
</syntaxhighlight>'''ملاحظة''': لا بد أن تأخذ في الحسبان السباق بين العلب المختلفة التي تحاول تحديث الذاكرة المؤقتة في الوقت ذاته، وذلك وفقًا لطريقة تهيئة المخزن المشترك.
 
== إحضار البيانات من جانب العميل ==
== إحضار البيانات من جانب العميل في Next.js ==
لهذا الأسلوب أهمية خاصة عندما لا تحتاج الصفحة إلى فهرستها لتحسين محركات البحث، أو إن كنت لا تريد تصيير بياناتك مسبقًا أو إن احتاج محتوى صفحاتك للتحديث باستمرار. وعلى خلاف الواجهة البرمجية لعملية التصيير من جانب الخادم، يمكنك إحضار البيانات من جانب العميل من خلال المكوّن نفسه.
لهذا الأسلوب أهمية خاصة عندما لا تحتاج الصفحة إلى فهرستها لتحسين محركات البحث، أو إن كنت لا تريد تصيير بياناتك مسبقًا أو إن احتاج محتوى صفحاتك للتحديث باستمرار. وعلى خلاف الواجهة البرمجية لعملية التصيير من جانب الخادم، يمكنك إحضار البيانات من جانب العميل من خلال المكوّن نفسه.


إن احضرت البيانات من خلال الصفحة، ستجري العملية خلال زمن التشغيل، ويُحدث محتوى الصفحة عندما تتغير البيانات. بينما إن أحضرت البيانات من خلال المكوّن، ستجري العملية خلال زمن تركيب المكوّن ومن ثم يُحدّث محتوى المكوِّن عندما تتغير البيانات.
إن أحضرت البيانات من خلال الصفحة، ستجري العملية خلال زمن التشغيل، ويُحدث محتوى الصفحة عندما تتغير البيانات. بينما إن أحضرت البيانات من خلال المكوّن، ستجري العملية خلال زمن تركيب المكوّن ومن ثم يُحدّث محتوى المكوِّن عندما تتغير البيانات.


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


=== استخدام الخطاف useEffect في إحضار البيانات من جانب العميل في Next.js ===
=== استخدام الخطاف useEffect في إحضار البيانات من جانب العميل ===
يعرض المثال التالي كيفية إحضار البيانات من جانب العميل باستخدام الخطاف <code>useEffect</code>:<syntaxhighlight lang="javascript">
يعرض المثال التالي كيفية إحضار البيانات من جانب العميل باستخدام الخطاف <code>[[React/hooks effect|useEffect]]</code>:<syntaxhighlight lang="javascript">
function Profile() {
function Profile() {
   const [data, setData] = useState(null)
   const [data, setData] = useState(null)
سطر 396: سطر 561:


=== إحضار البيانات من جانب العميل باستخدام المكتبة SWR ===
=== إحضار البيانات من جانب العميل باستخدام المكتبة SWR ===
أنشأ فريق تطوير مكتبة من خطافات رياكت لإحضار البيانات تًدعى [https://swr.vercel.app/ SWR]، ويُنصح باستخدامها لإحضار البيانات من جانب العميل. تتعامل هذه المكتبة مع التخزين المؤقت وإعادة التحقق وتتبع تركيز الدخل وضبط الفواصل الزمنية لإعادة إحضار البيانات وغيرها الكثير.
أنشأ فريق تطوير مكتبة من خطافات رياكت لإحضار البيانات تًدعى [https://swr.vercel.app/ SWR]، ويُنصح باستخدامها لإحضار البيانات من جانب العميل. تتعامل هذه المكتبة مع التخزين المؤقت وإعادة التحقق وتتبع تركيز الدخل وضبط الفواصل الزمنية لإعادة إحضار البيانات وغيرها الكثير.


سنستخدم في المثال التالي SWR في إحضار بيانات ملف شخصي، إذ تُخزِّن هذه المكتبة البيانات تلقائيًا وستعيد التحقق منها عندما تصبح قديمة.<syntaxhighlight lang="javascript">
سنستخدم في المثال التالي SWR في إحضار بيانات ملف شخصي، إذ تُخزِّن هذه المكتبة البيانات تلقائيًا وستعيد التحقق منها عندما تصبح قديمة.<syntaxhighlight lang="javascript">
سطر 421: سطر 586:


* صفحات [https://nextjs.org/docs/basic-features/data-fetching Data Fetching] في توثيق Next.js الرسمي.
* صفحات [https://nextjs.org/docs/basic-features/data-fetching Data Fetching] في توثيق Next.js الرسمي.
[[تصنيف:Next.js|{{SUBPAGENAME}}]]
[[تصنيف:Next.js Basic Features|{{SUBPAGENAME}}]]

المراجعة الحالية بتاريخ 05:38، 4 يناير 2023

تتيح عملية إحضار البيانات في Next.js تصيير المحتوى بطرق عدة وفقًا للحالة التي تستخدم فيها التطبيق، بما في ذلك التصيير المسبق من جانب الخادم Server-Side Rendering أو التوليد الساكن للشيفرة Static Generation أو تحديث المحتوى أو إنشائه أثناء تشغيل التطبيق من خلال التوليد الساكن التدريجي Incremental Static Regeneration.

الدالة غير المتزامنة getInitialProps

نصيحة هامة: يتيح لك استخدام إحدى الدالتين getStaticProps أو getServerSideProps بدلًا من getInitialProps في إحضار البيانات أسلوبًا يجمع بين التوليد الساكن والتصيير من جانب الخادم.

ملاحظة: قدّمت النسخة 13 من Next.js مجلدًا جديدًا يُدعى app/. ويدعم هذا المجلد إحضار البيانات من نفس المكان على صعيد المكوًنات باستخدام خطاف React الجديد use وواجهة الويب البرمجية الموّسعة fetch

تُمكّنك الدالة getInitialProps من تصيير المحتوى من جانب الخادم في الصفحة، وتسمح لك بنقل البيانات بشكل أولي، أي إرسال الصفحة بالبيانات التي أنتجها الخادم، وهذا مفيد خصوصًا في تحسين محركات البحث SEO.

ملاحظة: يُعطل استخدام الدالة getInitialProps التحسين التلقائي الساكن.

تُعد الدالة getInitialProps دالة غير متزامنة async يمكن إضافتها إلى أي صفحة كتابع ساكن static method. إليك مثالًا:

function Page({ stars }) {
  return <div>Next stars: {stars}</div>
}

Page.getInitialProps = async (ctx) => {
  const res = await fetch('https://api.github.com/repos/vercel/next.js')
  const json = await res.json()
  return { stars: json.stargazers_count }
}

export default Page

أو باستخدام مكوّن صنف:

import React from 'react'

class Page extends React.Component {
  static async getInitialProps(ctx) {
    const res = await fetch('https://api.github.com/repos/vercel/next.js')
    const json = await res.json()
    return { stars: json.stargazers_count }
  }

  render() {
    return <div>Next stars: {this.props.stars}</div>
  }
}

export default Page


تُستخدم الدالة getInitialProps لإحضار بعض البيانات بشكل غير متزامن لتحلل بعدها البيانات الجاهزة المعادة props من الدالة أثناء التصيير في الواجهة الخلفية بشكل مشابه لما يفعله التابع

JSON.stringify. تأكد أن الكائن الذي تعيده الدالة getInitialProps هو كائن بسيط لا يحتوي على الخاصيات Date أو Map أو Set.

تنفذ الدالة getInitialProps على الخادم فقط لتحميل الصفحة مبدئيًا، ثم تنفذ في الواجهة الأمامية عند الانتقال إلى وجهة أخرى باستخدام المكون next/link أو المكون next/router. لكن إن استخدمت الدالة getInitialProps تطبيقًا مخصصًا app.js_ وتضمنت الصفحة التي سننتقل إليها الدالة getServerSideProps عندها لن تعمل الدالة getInitialProps على الخادم.

الكائن context

تتلقى الدالة getInitialProps وسيطًا واحدًا يدعى context، وهو كائن يمتلك الخاصيات التالية:

  • pathname: الوجهة الحالية، وهي مسار الصفحة ضمن المجلد pages/.
  • query: قسم الاستعلام النصي من عنوان URL والمُحلَّل ككائن.
  • asPath: من النوع String، يمثل المسار الفعلي (بما في ذلك الاستعلام) الظاهر في شريط عنوان المتصفح.
  • req: كائن طلب HTTP (خادم فقط).
  • res: كائن طلب استجابة HTTP (خادم فقط).
  • err: كائن خطأ وينتج عن أي خطا يحدث أثناء التصيير.

التحفظات على استخدام الدالة

  • لا يمكن استخدام الدالة getInitialProps داخل المكوّنات الأبناء، بل فقط عند التصدير الافتراضي لكل صفحة.
  • إن كنت تستخدم وحدات الواجهة الخلفية فقط داخل الدالة getInitialProps، فتأكد من إدراجها بشكل صحيح وإلا ستبطئ تطبيقك.

ملاحظة: بغض النظر عن نوع التصيير ستُمرر أية خاصية props إلى مكون الصفحة وتُستعرض في الواجهة الأمامية كجزء من صفحة HTML المبدئية. إن الغاية من ذلك هو السماح بترطيب الصفحة hydrate بالشكل الصحيح. تأكد من عدم تمرير أية معلومات حساسة لا ينبغي عرضها في الواجهة الأمامية من خلال الخاصيات props.

استخدام الدالة مع TypeScript

إن كنت تستخدم TypeScript، بإمكانك استخدام النوع NextPage من أجل مكونات الدوال:

import { NextPage } from 'next'

interface Props {
  userAgent?: string;
}

const Page: NextPage<Props> = ({ userAgent }) => (
  <main>Your user agent: {userAgent}</main>
)

Page.getInitialProps = async ({ req }) => {
  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent
  return { userAgent }
}

export default Page

ويمكنك استخدام NextPageContext من أجل React.Component :

import React from 'react'
import { NextPageContext } from 'next'

interface Props {
  userAgent?: string;
}

export default class Page extends React.Component<Props> {
  static async getInitialProps({ req }: NextPageContext) {
    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent
    return { userAgent }
  }

  render() {
    const { userAgent } = this.props
    return <main>Your user agent: {userAgent}</main>
  }
}

التصيير من جانب الخادم باستخدام الدالة getServerSideProps

عندما تُصدِّر دالة تُدعى getServerSideProps (التصيير من جانب الخادم SSR) من صفحتك، تُصدَّر الصفحة مسبقًا عند كل طلب باستخدام البيانات التي تعيدها الدالة getServerSideProps:

export async function getServerSideProps(context) {
  return {
    props: {}, // تمرر إلى مكوّن الصفحة على شكل خصائص
  }
}

ملاحظة: بغض النظر عن نوع التصيير، أي شيء يُمرر إلى مكون الصفحة page ضمن الخاصيات props يُعرَض في طرف العميل ضمن صفحة HTML، وصحيح هذا يساعد على ترطيب الصفحة وتغذيها بالبيانات والمعلومات، ولكن كن حذرًا من تمرير أي بيانات حساسة لا يجب أن تُمرر إلى طرف العميل.

تنفيذ الدالة getServerSideProps

تُنفَّذ هذه الدالة من جانب الخادم ولا يمكن تنفيذها من قبل المتصفح، وما يحدث عندما تستخدمها الصفحة أنه:

  • عندما تطلب الصفحة مباشرة، ستُنفَّذ الدالة getServerSideProps وستُصيّر مسبقًا مع الخاصيات التي تعيدها الدالة.
  • عندما تطلب الصفحة من جانب العميل من خلال next/link أو next/router، سترسل Next.js طلبًا من خلال واجهة برمحية API إلى الخادم لكي ينفِّذ الدالة getServerSideProps.

تعيد الدالة getServerSideProps النتيجة على شكل بيانات بصيغة JSON تُستخدم في تصيير الصفحة، وستنجز Next.js كل هذه الأعمال تلقائيًا دون أن تفعل أي شيء إضافي عندما تُعرّف هذه الدالة.

يمكنك استخدام الأداة next-code-elimination tool للتحقق من شيفرة Next.js التي تُحذف عندما تُجمّع للعمل من ناحية العميل.

تُصدَّر الدالة getServerSideProps من الصفحات فقط، ولا يمكن تصديرها من ملفات لا تُعدُّ صفحاتٍ. وينبغي الانتباه إلى تصديرها كدالة قائمة بحد ذاتها standalone، فلن تعمل إن أضفتها كخاصية من خواص مكوّن الصفحة.

يمكنك معرفة جميع المعاملات والخاصيات التي تُستخدم مع getServerSideProps، بالاطلاع على مرجع الواجهة البرمجية API reference.

حالات استخدام الدالة getServerSideProps

استخدم هذه الدالة إن أردت أن تصيّر صفحة من المفترض إحضار بياناتها عند طلب هذه الصفحة. وقد يكون السبب هو طبيعة البيانات أو خصائص الطلب (مثل ترويسات الاستيثاق authorization أو الموقع الجغرافي). ستُصيّر الصفحات التي تستخدم getServerSideProps من جانب الخادم أثناء الطلب ولن تُخزَّن مؤقتًا ما لم تُهيَّأ ترويسات التحكم بالتخزين المؤقت.

إن لم تشأ أن تصير البيانات أثناء الطلب، فقد تفكّر في إحضارها من ناحية العميل أو باستخدام الدالة getStaticProps.

استخدام الدالة getServerSideProps مقابل مسارات الواجهة البرمجية API Route

قد يكون تلجأ إلى أسلوب إنشاء مسار API لإحضار البيانات من الخادم ثم استدعاء هذا المسار من خلال الدالة getServerSideProps لكن هذا الأسلوب غير ضروري وغير مجدٍ، لأنه سينفِّذ طلبًا إضافيًا نظرًا لتنفيذ الدالة getServerSideProps مع مسار الواجهة API معًا على الخادم.

لنفترض مثلًا وجود مسار API يُستخدم في إحضار بيانات من منظومة إدارة المحتوى CMS. يُستدعى هذا المسار عندها من قبل الدالة getServerSideProps مباشرة، وسينتج عن ذلك استدعاءً إضافيًا يخفض الأداء. بدلًا من ذلك أدرج تعليمات المنطق الذي يعتمده مسار API ضمن الدالة مباشرة، وقد يكون هذا المنطق استدعاءً لمنظومة إدارة المحتوى أو قاعدة بيانات أو مسارات API أخرى.

إحضار البيانات من جانب العميل

إن احتوت صفحتك على بيانات تُحدَث باستمرار ولا حاجة لتصييرها مسبقًا، بإمكانك عندها إحضار البيانات من جانب العميل وكمثال على سيناريوهات كهذه إحضار بيانات خاصة بالعميل:

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

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

استخدام الدالة getServerSideProps لإحضار البيانات عند الطلب

يعرض المثال التالي كيفية إحضار البيانات عند الطلب ثم تصيير البيانات مسبقًا قبل عرضها:

function Page({ data }) {
  // تصيير البيانات
}

// تُستدعى هذه الشيفرة عند كل طلب
export async function getServerSideProps() {
  // أحضر البيانات من وصلة برمجية خارجية
  const res = await fetch(`https://.../data`)
  const data = await res.json()

  // تمرير البيانات إلى الصفحة عن طريق الخاصيات
  return { props: { data } }
}

export default Page

التخزين المؤقت أثناء التصيير من جانب الخادم SSR

بإمكانك استخدام ترويسات التخزين المؤقت Cache-Control ضمن الدالة getServerSideProps لتخزين الاستجابات الديناميكية مؤقتًا. استخدم القيمة stale-while-revalidate مثلًا:

// (s-maxage=10)تُعد هذه القيمة جديدة مدة عشر ثوان.
// إن أعيد الطلب ضمن هذه الثواني العشرة ستبقى هذه القيمة حديثة
// بينما إن أعيد الطلب قبل 59 ثانية ستُصيّر القيمة على الرغم من كونها قديمة
// (stale-while-revalidate=59).
// سيجري طلب خلف الستار لتحديث  هذه القيمة في الذاكرة المؤقتة 
// سترى هذه القيمة الجديدة إن حدّثت الصفحة 
export async function getServerSideProps({ req, res }) {
  res.setHeader(
    'Cache-Control',
    'public, s-maxage=10, stale-while-revalidate=59'
  )

  return {
    props: {},
  }
}

تصيير الأخطاء عند استخدام الدالة getServerSideProps

إن ظهر خطأ داخل الدالة getServerSideProps فستعرض الملف pages/500.js. راجع توثيق الصفحة 500 لتتعلم طريقة إنشائها. لن يُستخدم هذا الملف في مرحلة التطوير وستُعرض الصفحة الخاصة بهذه المرحلة بدلًا منه.

التوليد الساكن للمسارات باستخدام الدالة getStaticPaths

عندما تمتلك الصفحة مسارات ديناميكية وتستخدم في الوقت نفسه الدالة getStaticProps، لا بد حينها من تعريف قائمة بالمسارات التي ينبغي توليدها بشكل ساكن، وعند تصدير الدالة التي تُدعى getStaticPaths (في آلية توليد الساكن للشيفرة) من صفحة تعتمد على مسارات ديناميكية، فستعمل Next.js آلية التصيير المسبق pre-render لتصيير جميع المسارات المعرفة عبر هذه الدالة.

// pages/posts/[id].js

// ‫يولد المسار `/posts/1` والمسار `/posts/2`
export async function getStaticPaths() {
  return {
    paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
    fallback: false, // can also be true or 'blocking'
  }
}

// ‫`getStaticPaths` يتطلب استعمال الدالة `getStaticProps`
export async function getStaticProps(context) {
  return {
    // Passed to the page component as props
    props: { post: {} },
  }
}

export default function Post({ post }) {
  // Render post...
}

يمكنك معرفة جميع المعاملات والخاصيات التي تُستخدم مع getStaticPaths، بالاطلاع على مرجع الواجهة البرمجية API reference.

حالات استخدام الدالة getStaticPaths

ينبغي استخدام هذه الدالة إن كنت ستصيّر مسبقًا صفحات تحتوي على مسارات ديناميكية وكان:

  • مصدر البيانات هو منظومة إدارة محتوى دون واجهة headless CMS.
  • مصدر البيانات هو قاعدة بيانات.
  • مصدر البيانات هو نظام إدارة ملفات.
  • بالإمكان التخزين المؤقت للبيانات للعموم (غير مخصصة لمستخدم محدد).
  • لا بدّ من تصيير الصفحة مسبقًا (لأغراض تحسين محركات البحث "سيو") وأن يجري ذلك بسرعة. تولّد getStaticProps ملفات HTML و JSON التي يمكن تخزينها مؤقتًا من قبل منظومة توصيل محتوى CDN لرفع الأداء.

تنفيذ الدالة getStaticPaths

تُنفَّذ هذه الدالة عند بناء التطبيق في وضع الإنتاج فقط. يمكنك التحقق من أن الشيفرة المكتوبة داخل الدالة getStaticPaths ستُزال من الشيفرة المجمّعة من ناحية العميل باستخدام الأداة next-code-elimination tool.

تنفيذ الدالة getStaticProps يتعلق بتنفيذ الدالة getStaticPaths وفق ما يلي:

  • تُنفَّذ الدالة getStaticProps عند تنفيذ الأمر next build لكل مسار paths يُعاد أثناء البناء.
  • تُنفَّذ الدالة getStaticProps في الخلفية عند استخدام التوجيه fallback: true.
  • تُستدعى الدالة getStaticProps قبل التصيير الأولي عند استخدام التوجيه fallback: blocking.

أماكن استخدام الدالة getStaticPaths

يمكن استخدام الدالة getStaticPaths في المواضع التالية:

  • تُستخدم مع الدالة .
  • لا يمكن استخدامها مع الدالة .
  • يمكن تصديرها من صفحة ذات مسارات ديناميكية تستخدم أيضًا الدالة .
  • لا يمكن تصديرها من ملف غير موجود ضمن مجلد الصفحات pages (مثلًا من مجلد المكونات components).
  • يجب تصديرها كدالة قائمة بحد ذاتها standalone، فلن تعمل إن أضفتها كخاصية من خواص مكوّن الصفحة.ملاحظة: تُستدعى الدالة عند كل استدعاء خلال مرحلة التطوير (next dev).

توليد المسارات عند الطلب

تتيح الدالة getStaticPaths إمكانية التحكم بأي صفحة تُولَّد أثناء عملية البناء بدلًا من توليدها عند الطلب on-demand مع الخيار fallback، إذ سيودي توليد الكثير من الصفحات إلى حدوث بطء في عملية البناء.

يمكنك تأجيل عملية توليد كل الصفحات عند الطلب بإعادة مصفوفة فارغة مع paths، وتبرز فائدة ذلك عند نشر تطبيق Next.js على أكثر من بيئة، إذ تصبح عملية البناء سريعة آنذاك في حال كنا نريد ذلك مثل تجربة التطبيق (أثناء التطوير وليس في بيئة الإنتاج)، وعلى أي حال هذا مفيد أيضًا في المواقع التي تتألف من مئات أو آلاف الصفحات الساكنة.

// pages/posts/[id].js

export async function getStaticPaths() {
  // في حال كنا نريد تجربة التطبيق في وضع الاستعراض، فلا يجري تصيير أي
  // صفحة ساكنة تصييرًا مسبقًا. وهذا يُسرع عملية البناء
  // ولكن تصبح عملية تحميل الصفحة الابتدائية بطيئة
  if (process.env.SKIP_BUILD_STATIC_GENERATION) {
    return {
      paths: [],
      fallback: 'blocking',
    }
  }

  // جلب جميع المنشورات عبر وصلة خارجية
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  // جلب جميع المسارات التي نريد تصييرها مسبقًا لما يقابل كل منشور
  // وصير كل الصفحات مسبقًا في بيئة الإنتاج، وهذا يُبطئ عملية البناء
  // ولكن يسرع من عملية تحميل الصفحة الابتدائية
  const paths = posts.map((post) => ({
    params: { id: post.id },
  }))

  // يعني أن بقية المسارات الأخرى تتوجه إلى الصفحة 404 { fallback: false }
  return { paths, fallback: false }
}

التوليد الساكن للصفحات باستخدام الدالة getStaticProps

عندما تُصدِّر دالة تُدعى getStaticProps (توليد ساكن للموقع) من صفحة، تُصيِّر Next.js هذه الصفحة أثناء بناء التطبيق باستخدام الخاصيات props التي تُعيدها هذه الدالة.

export async function getStaticProps(context) {
  return {
    props: {}, // تُمرر إلى مكوِّن الصفحة على شكل خاصيات
  }
}

ملاحظة: بغض النظر عن نوع التصيير، أي شيء يُمرر إلى مكون الصفحة page ضمن الخاصيات props يُعرَض في طرف العميل ضمن صفحة HTML، وصحيح هذا يساعد على ترطيب الصفحة وتغذيها بالبيانات والمعلومات، ولكن كن حذرًا من تمرير أي بيانات حساسة لا يجب أن تُمرر إلى طرف العميل.

حالات استخدام الدالة getStaticProps

عليك استخدام هذه الدالة في الحالات التالية:

  • البيانات التي ستستخدمها لتصيير الصفحة أثناء بناء التطبيق متوفرة مسبقًا قبل أن يطلبها المستخدم.
  • مصدر البيانات هو نظام إدارة محتوى دون واجهة headless CMS.
  • لا بدّ من تصيير الصفحة مسبقًا (لأغراض تحسين محركات البحث "سيو") وأن يجري ذلك بسرعة. تولّد getStaticProps ملفات HTML و JSON التي يمكن تخزينها مؤقتًا من قبل منظومة توصيل محتوى CDN لرفع الأداء.
  • عند إمكانية التخزين المؤقت للبيانات للعموم (غير مخصصة لمستخدم محدد). يمكن تجاوز هذا الشرط في بعض الحالات الخاصة من خلال استخدام برمجيات وسطية Middleware لإعادة كتابة المسار.

تنفيذ الدالة getStaticProps

تُنفَّذ هذه الدالة دائمًا من جانب الخادم وليس من جانب العميل ويمكنك التحقق من أن الشيفرة المكتوبة داخل الدالة getStaticProps ستُزال من الشيفرة المجمّعة من ناحية العميل باستخدام الأداة next-code-elimination tool:

  • تُنفَّذ getStaticProps دائمًا أثناء تنفيذ الأمر next build.
  • تُنفّذ getStaticProps في الخلفية عند استخدام التوجيه revalidate.
  • تُنفّذ getStaticProps في الخلفية عند الطلب عند استخدام revalidate()‎.

عندما تُستخدم الدالة getStaticProps مع نمط التجديد الساكن التدريجي للصفحات Incremental Static Regeneration، ستُنفَّذ في الخلفية أثناء إعادة تقييم الصفحة القديمة ومن ثم تُقدّم الصفحة المحدّثة إلى المتصفح.

لا يمكن للدالة getStaticProps أن تلج إلى الطلبات الواردة (كمعاملات الاستعلام أو ترويسات HTTP) لأنها تولّد ملف HTML ساكن، لكن إن أردت الوصول إلى هذه الطلبات فكِّر باستخدام برمجيات وسطية إضافة إلى هذه الدالة.

استخدام الدالة لإحضار البيانات من منظومة إدارة محتوى CMS

تعرض الشيفرة التالية طريقة إحضار قائمة بالمنشورات من منظومة إدارة محتوى:

// getStaticProps() تُعرض المنشورات أثناء بناء التطبيق باستخدام 
function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li>{post.title}</li>
      ))}
    </ul>
  )
}
// تُستدعى هذه الدالة أثناء بناء التطبيق من جانب الخادم
// لن تُستدعى من جانب العميل لذلك بالإمكان الاستعلام من قاعدة بيانات مباشرة

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

  // المنشورات كخاصيات أثناء بناء التطبيق Blog يتلقى المكوّن 
  // { props: { posts } } بإعادة
  return {
    props: {
      posts,
    },
  }
}

export default Blog

يمكنك معرفة جميع المعاملات والخاصيات التي تُستخدم مع getStaticProps، بالاطلاع على مرجع الواجهة البرمجية API reference.

كتابة شيفرة تعمل من جانب الخادم مباشرة

لن تُنفَّذ الدالة getStaticProps إلا من ناحية الخادم فقط ولا يمكن تنفيذها من ناحية العميل وبالتالي لن تجدها ضمن شيفرة جافا سكربت الخاصة بالمتصفح وستتمكن من كتابة استعلامات مباشرة من قاعدة البيانات دون إرسالها إلى المتصفحات. وهكذا يمكنك كتابة شيفرة تُنفَّذ من جانب الخادم مباشرة ضمن الدالة getStaticProps بدلًا من كتابة مسار واجهة API أي API route ليحضر تلك البيانات من واجهة خارجية عند استدعائها.

لنلق نظرة على المثال التالي: يُستخدم مسار API لإحضار بيانات من منظومة إدارة محتوى، لهذا سيُستدعى مباشرة من خلال الدالة getStaticProps، وستكون النتيجة استدعاء إضافي وانخفاضًا في الأداء. وبدلًا عن ذلك، يمكن استيراد منطق إحضار البيانات من منظومة إدارة المحتوى من المجلد lib/ مثلًا ومن ثم استدعائه مباشرة ضمن الدالة getStaticProps.

// lib/fetch-posts.js
// تُشارك الدالة التالية
// API routes و getStaticProps مع 
// `lib/` من المجلد
export async function loadPosts() {
  //لخارجية للحصول على المنشورات API تستدعى وصلة 
  const res = await fetch('https://.../posts/')
  const data = await res.json()

  return data
}

// pages/blog.js
import { loadPosts } from '../lib/load-posts'

// تُنفَّذ الدالة من جانب الخادم فقط
export async function getStaticProps() {
  //بالإمكان استدعاء نفس الدالة مباشرة `/api`  بدلًا من إحضار المسار 
  const posts = await loadPosts()

   // تُمرر إلى مكوِّن الصفحة على شكل خاصيات
  return { props: { posts } }
}

إن لم تستخدم مسارات API لإحضار البيانات بإمكانك استخدام الدالة ()fetch ضمن الدالة getStaticProps.

يمكنك التحقق مما سيُزال من الشيفرة المجمّعة من ناحية العميل باستخدام الأداة next-code-elimination tool.

التوليد التلقائي لشيفرة HTML و JSON

عندما تُصيَّر صفحة تحتوي على الدالة getStaticProps أثناء بناء التطبيق، ستولّد Next.js ملف JSON بالإضافة إلى ملف HTML وذلك لاحتواء نتيجة تنفيذ تلك الدالة.

يُستخدم ملف JSON ذاك للتوجيه من جانب العميل عبر next/link أو next/router. فعندما ننتقل إلى الصفحة التي صُيِّرت مسبقًا باستخدام الدالة getStaticProps، ستحضر Next.js ملف JSON (الذي بني مُسبقًا أثناء بناء التطبيق) ثم تسنده إلى خصائص مكوِّن الصفحة. وبالتالي لن تستدعي عمليات التنقل بين صفحات التطبيق من ناحية العميل استدعاء الدالة getStaticProps لأن ما سيُستخدم فعلًا هو ملف JSON المُصدَّر.

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

أماكن استخدام الدالة getStaticProps

يمكن تصدير الدالة getStaticProps من صفحة Next.js (مكوّن صفحة) فقط ولا يمكن تصديرها من ملفات لا تمثل صفحات. ومن أهم الأسباب الكامنة وراء هذا التقييد هو أن رياكت تحتاج إلى جميع البيانات قبل تصيير الصفحة.

لا بد أن تُصدّر أيضًا الدالة getStaticProps لتكون دالة قائمة بذاتها، فلن تعمل إن أضفتها كخاصية إلى مكوِّن الصفحة.

ملاحظة: إن أنشأت تطبيقًا مخصصًا فتأكد من تمرير pageProps إلى مكون الصفحة وإلا ستكون الخاصيات props فارغة.

تُستدعى الدالة عند كل استدعاء خلال مرحلة التطوير (next dev).

نمط الاستعراض

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

التجديد التدريجي الساكن

تتيح لك Next.js أن تنشئ أو تُحدِّث الصفحات الساكنة بعد أن تنهي بناء الموقع من خلال التجديد التدريجي الساكن Incremental Static Regeneration (واختصارًا ISR) الذي يمكِّنك من استخدام التجديد الساكن للصفحات منفردةً دون الحاجة إلى إعادة بناء الموقع بأكمله، وهكذا ستحتفظ بميزات الصفحات الساكنة مع توسيع إمكانات ملايين الصفحات.

لاستخدام 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

عندما تُطلب صفحة مصيّرة مسبقًا أثناء بناء التطبيق فستعرض مبدئيًا البيانات الموجودة في الذاكرة المؤقتة:

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

عندما يُطلب مسار محدد لم يُحدّث بعد، ستصيِّر الصفحة من جانب الخادم عند أول طلب، بينما ستُخدَّم الصفحة الساكنة عند ورود أية طلبات جديدة من الذاكرة المؤقتة.

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

عندما تضبط قيمة الخاصية revalidate على 60 ثانية، سيرى كل زوار صفحتك النسخة الموّلدة عن موقعك مدة دقيقة كاملة، والطريقة الوحيدة للتأكد من صحة بيانات الذاكرة المؤقتة هو زيارة الصفحة بعد مضي دقيقة.

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

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

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

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

استخدم بدايةً مفتاح استيثاق token يميزه تطبيقك وذلك لمنع الدخول غير المصرح به إلى مسار API الذي ستستخدمه لإعادة التحقق. يمكن الوصول إلى المسار (يدويًا أو عن طريق خُطافات Hook) من خلال هيكلة عنوان URL على الشكل التالي:

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

أضف المفتاح تاليًا على شكل متغير بيئة Environment Variable ثم أنشئ مسار 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: 'Invalid token' })
  }

  try {
    await res.revalidate('/path-to-revalidate')
    return res.json({ revalidated: true })
  } catch (err) {
    // في حال حدث خطأ، ستتابع في عرض آخر صفحة ولِّدت بشكل صحيح
   
    return res.status(500).send('Error revalidating')
  }
}

انظر هذا المثال.

اختبار التجديد التدريجي الساكن ISR حين الطلب في نمط التطوير

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

$ 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(`Failed to fetch posts, received status ${res.status}`)
  }

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

الاستضافة الذاتية والتجديد التدريجي الساكن ISR

يعمل التجديد التدريجي الساكن في مواقع Next.js التي تريد أن تديرها بنفسك بطريقة مختلفة عندما تستخدم next start. وبإمكانك استخدام هذا الأسلوب عندما تنشر الموقع على منسق حاويات container orchestrator مثل Kubernetes أو HashiCorp Nomad. تُخزّن الأصول assets افتراضيًا في الذاكرة ضمن كل علبة pod، ويعني ذلك وجود نسخة عن كل ملف ساكن في كل علبة. ستُعرض البيانات القديمة حتى تُستهدف علبة محددة بطلب.

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

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

module.exports = {
  experimental: {
   // افتراضيًا 50 ميغا بايت 
    isrMemoryCacheSize: 0,
  },
}

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

إحضار البيانات من جانب العميل

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

إن أحضرت البيانات من خلال الصفحة، ستجري العملية خلال زمن التشغيل، ويُحدث محتوى الصفحة عندما تتغير البيانات. بينما إن أحضرت البيانات من خلال المكوّن، ستجري العملية خلال زمن تركيب المكوّن ومن ثم يُحدّث محتوى المكوِّن عندما تتغير البيانات.

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

استخدام الخطاف useEffect في إحضار البيانات من جانب العميل

يعرض المثال التالي كيفية إحضار البيانات من جانب العميل باستخدام الخطاف useEffect:

function Profile() {
  const [data, setData] = useState(null)
  const [isLoading, setLoading] = useState(false)

  useEffect(() => {
    setLoading(true)
    fetch('api/profile-data')
      .then((res) => res.json())
      .then((data) => {
        setData(data)
        setLoading(false)
      })
  }, [])

  if (isLoading) return <p>Loading...</p>
  if (!data) return <p>No profile data</p>

  return (
    <div>
      <h1>{data.name}</h1>
      <p>{data.bio}</p>
    </div>
  )
}

إحضار البيانات من جانب العميل باستخدام المكتبة SWR

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

سنستخدم في المثال التالي SWR في إحضار بيانات ملف شخصي، إذ تُخزِّن هذه المكتبة البيانات تلقائيًا وستعيد التحقق منها عندما تصبح قديمة.

import useSWR from 'swr'

const fetcher = (...args) => fetch(...args).then((res) => res.json())

function Profile() {
  const { data, error } = useSWR('/api/profile-data', fetcher)

  if (error) return <div>Failed to load</div>
  if (!data) return <div>Loading...</div>

  return (
    <div>
      <h1>{data.name}</h1>
      <p>{data.bio}</p>
    </div>
  )
}

المصادر