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

من موسوعة حسوب
لا ملخص تعديل
طلا ملخص تعديل
 
(14 مراجعة متوسطة بواسطة مستخدمين اثنين آخرين غير معروضة)
سطر 3: سطر 3:


== الدالة غير المتزامنة <code>getInitialProps</code> ==
== الدالة غير المتزامنة <code>getInitialProps</code> ==
'''نصيحة هامة''': يتيح لك استخدام إحدى الدالتين [[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/pages|الصفحة]]، وتسمح لك بنقل البيانات بشكل أولي، أي إرسال الصفحة بالبيانات التي أنتجها الخادم، وهذا مفيد خصوصًا في تحسين محركات البحث SEO.


'''ملاحظة''': يُعطل استخدام الدالة <code>getInitialProps</code> [[Next.js/automatic static optimization|التحسين التلقائي الساكن]]
'''ملاحظة''': يُعطل استخدام الدالة <code>getInitialProps</code> [[Next.js/automatic static optimization|التحسين التلقائي الساكن]].


تُعد الدالة <code>getInitialProps</code> دالة غير متزامنة <code>async</code> يمكن إضافتها إلى أي صفحة كتابع ساكن <code>[https://javascript.info/static-properties-methods static method]</code>. إليك مثالًا:<syntaxhighlight lang="javascript">
تُعد الدالة <code>getInitialProps</code> دالة غير متزامنة <code>async</code> يمكن إضافتها إلى أي صفحة كتابع ساكن <code>[https://javascript.info/static-properties-methods static method]</code>. إليك مثالًا:<syntaxhighlight lang="javascript">
سطر 37: سطر 37:


export default Page
export default Page
</syntaxhighlight>تُستخدم الدالة <code>getInitialProps</code> لإحضار بعض البيانات بشكل غير متزامن لتحلل بعدها البيانات الجاهزة المعادة <code>props</code> من الدالة أثناء التصيير في الواجهة الخلفية بشكل مشابه لما يفعله التابع <code>[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify JSON.stringify]</code>. تأكد أن الكائن الذي تعيده الدالة <code>getInitialProps</code> هو كائن بسيط لا يحتوي على الخاصيات <code>Date</code> أو <code>Map</code> أو <code>Set</code>.
</syntaxhighlight>تُستخدم الدالة <code>getInitialProps</code> لإحضار بعض البيانات بشكل غير متزامن لتُحلَّل بعدها البيانات الجاهزة المعادة <code>props</code> من الدالة أثناء التصيير في الواجهة الخلفية بشكل مشابه لما يفعله التابع <code>[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify 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>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>context</code> ===
سطر 46: سطر 46:
* <code>pathname</code>: الوجهة الحالية، وهي مسار الصفحة ضمن المجلد <code>pages/</code>.
* <code>pathname</code>: الوجهة الحالية، وهي مسار الصفحة ضمن المجلد <code>pages/</code>.
* <code>query</code>: قسم الاستعلام النصي من عنوان URL والمُحلَّل ككائن.
* <code>query</code>: قسم الاستعلام النصي من عنوان URL والمُحلَّل ككائن.
* <code>asPath</code>: من النوع <code>String</code>، يمثل المسار الفعلي (بما في ذلك الاستعلام) الظاهر في شريط عنوان المتصفح.
* <code>asPath</code>: من النوع <code>String</code>، ويمثل المسار الفعلي (بما في ذلك الاستعلام) الظاهر في شريط عنوان المتصفح.
* <code>req</code>: [[Node.js/http|كائن طلب HTTP]] (خادم فقط).
* <code>req</code>: [[Node.js/http|كائن طلب HTTP]] (خادم فقط).
* <code>res</code>: [[Node.js/http|كائن طلب استجابة HTTP]] (خادم فقط).
* <code>res</code>: [[Node.js/http|كائن استجابة HTTP]] (خادم فقط).
* <code>err</code>: كائن خطأ وينتج عن أي خطا يحدث أثناء التصيير.
* <code>err</code>: كائن خطأ وينتج عن أي خطا يحدث أثناء التصيير.


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


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


<blockquote>ملاحظة: بغض النظر عن نوع التصيير ستُمرر أية خاصية <code>props</code> إلى مكون الصفحة وتُستعرض في الواجهة الأمامية كجزء من صفحة HTML المبدئية. إن الغاية من ذلك هو السماح [[React/react dom#hydrate.28.29|بترطيب الصفحة]] hydrate بالشكل الصحيح. تأكد من عدم تمرير أية معلومات حساسة لا ينبغي عرضها في الواجهة الأمامية من خلال الخاصيات <code>props</code>.</blockquote>
<blockquote>'''ملاحظة''': بغض النظر عن نوع التصيير ستُمرَّر أية خاصية <code>props</code> إلى مكون الصفحة وتُستعرض في الواجهة الأمامية كجزء من صفحة HTML المبدئية. إن الغاية من ذلك هو السماح [[React/react dom#hydrate.28.29|بترطيب الصفحة]] hydrate بالشكل الصحيح. تأكد من عدم تمرير أية معلومات حساسة لا ينبغي عرضها في الواجهة الأمامية من خلال الخاصيات <code>props</code>.</blockquote>


=== استخدام الدالة مع TypeScript ===
=== استخدام الدالة مع TypeScript ===
إن كنت تستخدم TypeScript، بإمكانك استخدام النوع <code>NextPage</code> من أجل مكونات الدوال: <syntaxhighlight lang="javascript">
إن كنت تستخدم TypeScript، بإمكانك استخدام النوع <code>NextPage</code> من أجل مكوِّنات الدوال: <syntaxhighlight lang="javascript">
import { NextPage } from 'next'
import { NextPage } from 'next'


سطر 76: سطر 76:


export default Page
export default Page
</syntaxhighlight>ويمكنك استخدام <code>NextPageContext</code> من أجل <code>React.Component</code> :<syntaxhighlight lang="javascript">
</syntaxhighlight>ويمكنك استخدام <code>NextPageContext</code> من أجل <code>React.Component</code>:<syntaxhighlight lang="javascript">
import React from 'react'
import React from 'react'
import { NextPageContext } from 'next'
import { NextPageContext } from 'next'
سطر 95: سطر 95:
   }
   }
}
}
</syntaxhighlight>
== الدالة غير المتزامنة <code>getServerSideProps</code> ==
عندما تُصدِّر الصفحة دالة تُدعى <code>getServerSideProps</code> (تصيير من جانب الخادم)، تُصيَّر هذه الصفحة مسبقًا عند كل طلب باستخدام البيانات المعادة من هذه الدالة. ولهذا الأمر فائدته إن كنت تريد إحضار البيانات التي تتغير أحيانًا، وتحديث الصفحة لعرض آخر حالة للبيانات:<syntaxhighlight lang="javascript">
export async function getServerSideProps(context) {
  return {
    props: {}, // تُمرر إلى مكون الصفحة على شكل خاصيات
  }
}
</syntaxhighlight>بإمكانك إدراج الوحدات البرمجية في أعلى مستوى كي تستخدمها مع <code>getServerSideProps</code>، ولن تُجمّع هذه الوحدات ضمن شيفرة جانب العميل (الواجهة الأمامية). أي بإمكانك كتابة الشيفرة التي تُنفّذ على الخادم مباشرة ضمن <code>getServerSideProps</code> بما في ذلك إحضار البيانات من قاعدة بيانات.
=== الكائن <code>context</code> ===
تتلقى الدالة <code>getServerSideProps</code> معاملًا واحدًا يدعى <code>context</code>، وهو كائن يمتلك المفاتيح التالية:
* <code>params</code>: إن استخدمت الصفحة مسارات ديناميكية، سيضم هذا المفتاح معاملات الوجهة. إذ تبدو هذه المعاملات على الشكل <code>{ ...:id}</code> إن كان اسم الصفحة مثلًا <code>[id].js</code>
* <code>query</code>: قسم الاستعلام النصي من عنوان URL والمُحلَّل ككائن.
* <code>req</code>: [[Node.js/http|كائن الرسائل الواردة في HTTP]] مع الخاصية الإضافية <code>cookies</code>، وهي كائن له مفاتيح نصية ترتبط مع القيم النصية لملفات تعريف الارتباط.
* <code>res</code>: [[Node.js/http|كائن استجابة HTTP]] (خادم فقط).
* <code>preview</code>: يأخذ القيمة <code>true</code> عندما تكون الصفحة في [[Next.js/preview mode|وضع الاستعراض]].
* <code>previewData</code>: بيانات الاستعراض التي تضبطها <code>setPreviewData</code>.
* <code>resolvedUrl</code>: نسخة عادية من عنوان <code>URL</code> المطلوب بعد تجريده من بادئة <code>next/data_</code> وذلك للتنقل في طرف العميل، وتتضمن قيم الاستعلام الأصلي.
* <code>locale</code>: يضم الإعداد المحلي الفعّال (إن مُكِّن).
* <code>locales</code>: يضم كل الإعدادات المحلية المدعومة (إن مُكِّن).
* <code>defaultLocale</code>: يضم كل الإعدادات المحلية الافتراضية المهيأة (إن مُكِّن).
=== القيم التي تعيدها الدالة <code>getServerSideProps</code> ===
لا بد أن تعيد هذه الدالة كائنًا له إحدى الخاصيات التالية:
==== الخاصية <code>props</code> ====
وهو كائن من الشكل (مفتاح-قيمة) يتلقى كل قيمه من مكوّن الصفحة. ينبغي أن يكون الكائن قابلًا للتحليل كي تُحلل الخاصيات الممررة باستخدام التابع <code>JSON.stringify</code>.<syntaxhighlight lang="javascript">
export async function getServerSideProps(context) {
  return {
    props: { message: `Next.js is awesome` }, // تمرر إلى مكون الصفحة كخاصيات
  }
}
</syntaxhighlight>
==== الخاصية <code>notFound</code> ====
وهي قيمة منطقية تتيح للصفحة أن تعيد رمز الحالة 404 و<nowiki/>[[Next.js/custom error page|صفحة هذه الحالة]]. تُعيد الصفحة عندما تكون قيمة هذه الخاصية <code>true</code> رمز الحالة <code>404</code> حتى لو وُلِّدت بنجاح سابقًا. إن الغاية من ذلك دعم بعض حالات الاستخدام مثل المحتوى الذي يولّده مستخدم ثم يُزيله.<syntaxhighlight lang="javascript">
export async function getServerSideProps(context) {
  const res = await fetch(`https://.../data`)
  const data = await res.json()
  if (!data) {
    return {
      notFound: true,
    }
  }
  return {
    props: { data }, // يُمرر إلى مكوّن الصفحة كخاصيات
  }
}
</syntaxhighlight>
==== الخاصية <code>redirect</code> ====
يتيح الكائن <code>redirect</code> إعادة التوجيه إلى مصادر داخلية أو خارجية، وينبغي أن يطابق الشكل <code>{ destination: string, permanent: boolean }</code>. قد تحتاج في حالات نادرة تعيين رموز حالة مخصصة لعملاء <code>HTTP</code> الأقدم كي يُعاد توجيههم بالشكل الصحيح. في حالات كهذه، استخدم الخاصية <code>statusCode</code> بدلًا من <code>permanent</code> لكن لا تستخدمهما معًا:<syntaxhighlight lang="javascript">
export async function getServerSideProps(context) {
  const res = await fetch(`https://.../data`)
  const data = await res.json()
  if (!data) {
    return {
      redirect: {
        destination: '/',
        permanent: false,
      },
    }
  }
  return {
    props: {}, //يُمرر إلى مكوّن الصفحة كخاصيات
  }
}
</syntaxhighlight>
==== استخدام الدالة مع TypeScript ====
بإمكانك استخدام النوع <code>GetServerSideProps</code> من المكتبة <code>next</code>:<syntaxhighlight lang="javascript">
import { GetServerSideProps } from 'next'
export const getServerSideProps: GetServerSideProps = async (context) => {
  // ...
}
</syntaxhighlight>وإن كنت تريد استخدام الأنواع الاستدلالية inferred typings للخاصيات، بإمكانك استخدام <code><InferGetServerSidePropsType<typeof getServerSideProps</code>:<syntaxhighlight lang="javascript">
import { InferGetServerSidePropsType } from 'next'
type Data = { ... }
export const getServerSideProps = async () => {
  const res = await fetch('https://.../data')
  const data: Data = await res.json()
  return {
    props: {
      data,
    },
  }
}
function Page({ data }: InferGetServerSidePropsType<typeof getServerSideProps>) {
  // سيحلل المنشورات وفق نوع البيانات
}
export default Page
</syntaxhighlight>ستعمل الأنواع الضمنية أيضًا مع الدالة  <code>GetServerSideProps</code>:<syntaxhighlight lang="javascript">
import { InferGetServerSidePropsType } from 'next'
type Data = { ... }
export const getServerSideProps = async () => {
  const res = await fetch('https://.../data')
  const data: Data = await res.json()
  return {
    props: {
      data,
    },
  }
}
function Page({ data }: InferGetServerSidePropsType<typeof getServerSideProps>) {
  // سيحلل البيانات إلى النوع المطلوب
}
export default Page
</syntaxhighlight>
== الدالة غير المتزامنة <code>getStaticPaths</code> ==
عندما تُصدِّر الصفحة دالة تُدعى <code>getStaticPaths</code> من صفحة تستخدم مسارات ديناميكية، تُصيِّر هذه الصفحة مسبقًا وبشكل ساكن كل المسارات التي تحددها الدالة.<syntaxhighlight lang="javascript">
export async function getStaticPaths() {
return {
    paths: [
      { params: { ... } } //في الأسفل "pathes" اطلع على القسم
    ],
    fallback: true, false or "blocking" // في الأسفل "fallback" اطلع على
  };
}
</syntaxhighlight>
=== القيم التي تعيدها الدالة <code>getStaticPaths</code> ===
لا بد أن تعيد هذه الدالة كائنًا له الخاصيات المطلوبة التالية:
==== الخاصية <code>path</code> ====
وهي مفتاح يحدد المسارات التي تُصيّر مسبقًا. فلو افترضنا مثلًا وجود صفحة تستخدم وجهات ديناميكية على النحو <code>pages/posts/[id].js</code>. فلو صَدّّرت عندها الدالة <code>getStaticPaths</code> من الصفحة وأعدت المسارات <code>paths</code> كالتالي:<syntaxhighlight lang="javascript">
return {
  paths: [
    { params: { id: '1' }},
    {
      params: { id: '2' },
      // with i18n configured the locale for the path can be returned as well
      locale: "en",
    },
  ],
  fallback: ...
}
</syntaxhighlight>ستوّلد Next.js بشكل ساكن الصفحات <code>posts/1/</code> و <code>posts/2/</code> أثناء تنفيذ الأمر <code>next build</code> باستخدام مكوّن الصفحة في <code>pages/posts/[id].js</code>.
لا بد من تطابق قيمة كل كائن <code>params</code> مع المعاملات المستخدمة في تسمية الصفحة:
* إن كان اسم الصفحة <code>pages/posts/[postId]/[commentId]</code>، ينبغي أن يتضمن عندها الكائن <code>params</code> الخاصيتين <code>postId</code> و <code>commentId</code>.
* إن استخدمت [[Next.js/Routing|تقنية التقاط كل الوجهات]] في تسمية الصفحة مثل <code>pages/[...slug]</code>، لا بد أن يتضمن عندها الكائن <code>params</code> المصفوفة <code>slug</code>. فلو كانت هذه المصفوفة مثلًا <code>['hello', 'world']</code>، ستوّلد Next.js بشكل ساكن الصفحة التي عنوانها <code>hello/world/</code>
* إن استخدمت الصفحة تقنية التقاط جميع الوجهات اختياريًا، استخدم <code>null</code> أو <code>[]</code> أو <code>undefined</code> أو <code>false</code> لتصيير الوجهة الأكثر قربًا من جذر التطبيق. فلو استخدمت <code>slug: false</code> للصفحات <code>pages/<nowiki>[[...slug]]</nowiki></code>، ستولَّد الصفحة في جذر التطبيق <code>/</code>.
إن المعامل <code>params</code> حساس لحالة الأحرف ومن الأفضل كتابته بأحرف صغيرة للتأكد من توليد المسارات بالشكل الصحيح. فلو أعيدت الكلمة <code>WoRLD</code> إلى معامل ما، ستطابق الحالة التي يكون فيها المسار الفعلي هو <code>WoRLD</code> وليس <code>world</code> أو <code>World</code> .
وبمعزل عن الكائن <code>params</code>، يمكن إعادة حقل الإعداد المحلي <code>locale</code> عندما يُهيأ [[Next.js/i18n routing|التوجه i18n]] الذي يضبط بدوره الإعداد المحلي للمسار الذي يُولَّد.
==== الخاصية <code>fallback: false</code> ====
إن كانت قيمة الخاصية <code>fallback</code> هي <code>false</code> فستكون الصفحة 404 هي نتيجة كل مسار لا تعيده الدالة <code>getStaticPaths</code>. عندما تُنفِّذ الأمر <code>next build</code>، تكتشف Next.js إن أعادت الدالة <code>getStaticPaths</code> القيمة <code>fallback: false</code>، كي توّلد عندها المسارات التي تعيدها الدالة <code>getStaticPaths</code> فقط.
تظهر فائدة الأمر إن كان لديك عدد محدود من المسارات التي عليك إنشاؤها، أو في الحالة التي لن تضيف فيها بيانات جديدة للصفحة غالبًا. لكن إن رأيت أنك تحتاج إلى مسارات أكثر وكان <code>fallback: false</code>، لا بد عندها من تنفيذ الأمر <code>next build</code> مجددًا لكي تولَّد المسارات الجديدة.
يصيّر المثال التالي منشورًا واحدًا في الصفحة يُدعى <code>pages/posts/[id].js</code>. إذ تُجلب قائمة منشورات المدونة من منظومة إدارة محتوى CMS وتُعاد من قبل الدالة <code>getStaticPaths</code>. عندها ستحضر هذه الدالة ولكل صفحة بيانات المنشور من منظومة إدارة المحتوى باستخدام الدالة <code>getStaticProps</code>:<syntaxhighlight lang="javascript">
// pages/posts/[id].js
function Post({ post }) {
  // تصيير المنشور...
}
// تُستدعى هذه الدالة أثناء البناء
export async function getStaticPaths() {
  //خارجية للحصول على المنشورات API استدعي وصلة
  const res = await fetch('https://.../posts')
  const posts = await res.json()
  // الحصول على المسارات التي تريد تصييرها مسبقًا وفقًل للمنشورات
  const paths = posts.map((post) => ({
    params: { id: post.id },
  }))
  // سنصيّر مسبقًا هذه المسارات فقط أثناء البناء
  // { fallback: false } تعيد بقية المسارات الصفحة 404 لأن
  return { paths, fallback: false }
}
// تُستدعى أيضًا أثناء البناء
export async function getStaticProps({ params }) {
  //للمنشور id المعرف  params يتضمن
  // 1 هو params.id سيكون /posts/1 في حال
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()
  // تمرير بيانات المنشور إلى الصفحة كخاصيات
  return { props: { post } }
}
export default Post
</syntaxhighlight>
==== الخاصية <code>fallback: true</code>====
إن كانت قيمة <code>fallback</code> هي <code>true</code> سيتغير سلوك الدالة <code>getStaticProps</code> على النحو التالي:
* تصيَّر المسارات المعادة من قبل الدالة <code>getStaticProps</code> إلى <code>HTML</code> أثناء البناء بواسطة الدالة <code>getStaticProps</code>.
* لن ينتج عن المسارات التي لم توّلد أثناء البناء صفحات 404، بل ستقدّم نسخة تراجع عن الصفحة عند أول طلب إلى مسار كهذا. لن تُخدّم زواحف ويب Web crawler (مثل زواحف Google) بنسخة تراجع، وسيسلك المسار سلوك الحالة <code>'fallback: 'blocking</code> التي سنراها تاليًا.
* عندما ننتقل إلى صفحة فيها <code>fallback: true</code> من خلال الكائنين <code>next/link</code> أو <code>next/router</code>، فلن تُخدّم Next.js الطلب بصفحة تراجع بل ستسلك الصفح سلوك الحالة  <code>fallback: 'blocking'‎</code>.
* توّلد Next.js في الخلفية وبشكل ساكن بيانات <code>HTML</code> و <code>JSON</code> للمسار المطلوب، ويتضمن ذلك تنفيذ الدالة <code>getStaticProps</code>.
* عندما تكتمل العملية، يتلقى المتصفح بيانات <code>JSON</code> للمسار المولّد والتي تُستخدم في التصيير التلقائي للصفحة بالخاصيات المطلوبة. ومن وجهة نظر المستخدم، ستتبدل الصفحة من صفحة تراجع إلى صفحة كاملة.
* في الوقت ذاته، تضيف Next.js هذا المسار إلى الصفحات التي تصيَّر مسبقًا، وتُخدَّّم الطلبات اللاحقة لهذا المسار من الصفحة التي ولِّدت كغيرها من الصفحات التي تُصيّر مسبقًا أثناء البناء.
<blockquote>'''ملاحظة''': لا يمكن استخدام <code>fallback: true</code> مع <code>next export</code>.</blockquote>
==== متى تستفيد من الخاصية <code>fallback: true</code> ====
تظهر فائدة <code>fallback: true</code> عندما يضم التطبيق عددًا كبيرًا من الصفحات الساكنة التي تعتمد على البيانات. فلو أردت تصيير جميع الصفحات في التطبيق ستسغرق عملية البناء وقتًا طويلًا جدًا.
بدلًا من ذلك، بإمكانك توليد مجموعة صغيرة من الصفحات واستخدام <code>fallback: true</code> للبقية. فعندما يطلب أحدهم صفحة لم تولّد بعد، سيرى هذه الصفحة وهي تحمل مؤشر التحميل أو مكوّن هيكل الصفحة. وبعد فترة وجيزة، ينتهي تنفيذ <code>getStaticProps</code> وتصُيّر الصفحة بالبيانات المطلوبة. بعد ذلك سيحصل كل من يطلب هذه الصفحة على هذه النسخة المصيّرة مسبقًا، مما يضمن تجربة سريعة للمستخدم مع المحافظة على سرعة البناء ومزايا التوليد الساكن.
لا تُحدِّث <code>fallback: true</code> الصفحات المولّدة، لهذا ننصحك بإلقاء نظرة على [[Next.js/data fetching#.D8.A7.D9.84.D8.AA.D8.AC.D8.AF.D9.8A.D8.AF%20.D8.A7.D9.84.D8.AA.D8.AF.D8.B1.D9.8A.D8.AC.D9.8A%20.D8.A7.D9.84.D8.B3.D8.A7.D9.83.D9.86%20.D9.81.D9.8A%20Next.js|التجديد التدريجي الساكن]].
==== الخاصية <code>'fallback: 'blocking</code> ====
إن كانت قيمة <code>fallback</code> هي <code>'blocking'</code>، تنتظر المسارات الجديدة التي لم تعيدها الدالة <code>getStaticPaths</code> حتى تولَّد صفحات <code>HTML</code> كما هو الحال عند التصيير في جانب الخادم، ثم تُخزّن مؤقتًا للطلبات اللاحقة أي أنها تُصيَّر مرة لكل مسار.
تسلك الدالة <code>getStaticProps</code> السلوك التالي:
* تُصيَّر المسارات التي تعيدها <code>getStaticPaths</code> إلى صفحات <code>HTML</code> أثناء البناء بواسطة الدالة <code>getStaticProps</code>.
* لن ينتج عن المسارات التي لم تولَّد أثناء البناء صفحات 404، بل تصييرها Next.js في جانب الخادم عند أول طلب وتعيد صفحة <code>HTML</code> الناتجة.
* عندما تكتمل العملية، يتلقى المتصفح بيانات <code>HTML</code> للمسار المولّد. ومن وجهة نظر المستخدم، ستتبدل الحالة من "المتصفح يطلب الصفحة" إلى "تحميل الصفحة بالكامل"، ولن تكون هناك حالة تحميل أو تراجع.
* في الوقت ذاته، تضيف Next.js هذا المسار إلى الصفحات التي تصيَّر مسبقًا، وتُخدَّّم الطلبات اللاحقة لهذا المسار من الصفحة التي ولِّدت كغيرها من الصفحات التي تُصيّر مسبقًا أثناء البناء.
لا تُحدّث <code>'fallback: 'blocking</code> الصفحات المولّدة، لهذا عليك استخدام [[Next.js/data fetching#.D8.A7.D9.84.D8.AA.D8.AC.D8.AF.D9.8A.D8.AF%20.D8.A7.D9.84.D8.AA.D8.AF.D8.B1.D9.8A.D8.AC.D9.8A%20.D8.A7.D9.84.D8.B3.D8.A7.D9.83.D9.86%20.D9.81.D9.8A%20Next.js|التجديد التدريجي الساكن]] على التوازي معها.<blockquote>'''ملاحظة''': لا يمكن استخدام <code>'fallback: 'blocking</code> مع <code>next export</code>.</blockquote>
=== صفحات التراجع Fallback ===
في نسخة التراجع fallback من الصفحة:
* ستكون خاصيات الصفحة فارغة.
* باستخدام [[Next.js/next router|الموّجه]]، يمكنك التحقق من تصيير صفحة التراجع، إذ ستكون قيمة <code>router.isFallback</code> هي <code>true</code>.
يعرض المثال التالي حالات عن استخدام <code>isFallback</code>:<syntaxhighlight lang="javascript">
// pages/posts/[id].js
import { useRouter } from 'next/router'
function Post({ post }) {
  const router = useRouter()
  // إن لم تولّد الصفحة بعدن سيُعرض التالي بشكل مبدئي حتى ينتهي تنفيذ الدالة
  // getStaticProps()
  if (router.isFallback) {
    return <div>Loading...</div>
  }
  // تصيير المنشور...
}
// تُستدعى هذه الدالة أثناء البناء
export async function getStaticPaths() {
  return {
    //أثناء البناء `/posts/1` و `/posts/2` يُولّد فقط
    paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
    // تمكين التوليد الستاتيكي لصفحات إضافية
    // مثلًا `/posts/3`
    fallback: true,
  }
}
// يُستدعى هذا أيضًا أثناء البناء
export async function getStaticProps({ params }) {
  //للمنشور id المعرف  params يتضمن
  // 1 هو params.id سيكون /posts/1 في حال
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()
  // تمرير بيانات المنشورات إلى الصفحة عبر الخاصيات
  return {
    props: { post },
    // تجديد المنشور على الأكثر مرة كل ثانية
    revalidate: 1,
  }
}
export default Post
</syntaxhighlight>
=== استخدام <code>getStaticPaths</code> مع TypeScript ===
بإمكانك استخدام النوع <code>GetStaticPaths</code> من المكتبة <code>next</code>:<syntaxhighlight lang="javascript">
import { GetStaticPaths } from 'next'
export const getStaticPaths: GetStaticPaths = async () => {
  // ...
}
</syntaxhighlight>
== الدالة غير المتزامنة <code>getStaticProps</code> ==
يؤدي تصدير دالة تُدعى <code>getStaticProps</code> إلى التصيير المسبق للصفحة أثناء البناء باستخدام الخاصيات التي تُعيدها الدالة.<syntaxhighlight lang="javascript">
export async function getStaticProps(context) {
  return {
    props: {}, //تُمرر إلى الصفحة على شكل خاصيات
  }
}
</syntaxhighlight>بإمكانك إدراج الوحدات البرمجية في أعلى مستوى كي تستخدمها مع <code>getStaticProps</code>، ولن تُجمّع هذه الوحدات ضمن شيفرة جانب العميل (الواجهة الأمامية). أي بإمكانك كتابة الشيفرة التي تُنفّذ على الخادم مباشرة ضمن <code>getStaticProps</code> بما في ذلك إحضار البيانات من قاعدة بيانات.
=== الكائن <code>context</code> ===
تتلقى الدالة <code>getStaticProps</code> معاملًا واحدًا يدعى <code>context</code>، وهو كائن يمتلك المفاتيح التالية:
* <code>params</code>: إن استخدمت الصفحة مسارات ديناميكية، سيضم هذا المفتاح معاملات الوجهة. إذ تبدو هذه المعاملات على الشكل <code>{ ...:id}</code> إن كان اسم الصفحة مثلًا <code>[id]</code>. ينبغي أن تستخدم هذه المعاملات مع <code>getStaticPaths</code>، وهذا ما سنشرحه لاحقًا.
* <code>preview</code>: يأخذ القيمة <code>true</code> عندما تكون الصفحة في [[Next.js/preview mode|وضع الاستعراض]].
* <code>previewData</code>: بيانات الاستعراض التي تضبطها <code>setPreviewData</code>.
* <code>locale</code>: يضم الإعداد المحلي الفعّال (إن مُكِّن).
* <code>locales</code>: يضم كل الإعدادات المحلية المدعومة (إن مُكِّن).
* <code>defaultLocale</code>: يضم كل الإعدادات المحلية الافتراضية المهيأة (إن مُكِّن).
=== القيم التي تعيدها الدالة <code>getStaticProps</code> ===
لا بد أن تعيد هذه الدالة كائنًا له إحدى الخاصيات التالية <code>props</code> أو <code>redirect</code> أو <code>notFound</code> متبوعة بالخاصية الاختيارية <code>revalidate</code>.
==== الخاصية <code>props</code> ====
وهو كائن من الشكل (مفتاح-قيمة) يتلقى قيمه من مكوّن الصفحة. ينبغي أن يكون الكائن قابلًا للتحليل كي تُحلل الخاصيات الممررة باستخدام التابع <code>JSON.stringify</code>.<syntaxhighlight lang="javascript">
export async function getStaticProps(context) {
  return {
    props: { message: `Next.js is awesome` }, // تمرر إلى مكون الصفحة كخاصيات
  }
}
</syntaxhighlight>
==== الخاصية <code>revalidate</code> ====
وتمثّل عدد الثواني التي يمكن أن يحدث بعدها تجديد للصفحة (تأخذ القيمة <code>false</code> افتراضيًا، أي لا إعادة تحقق):<syntaxhighlight lang="javascript">
// تُستدعى هذه الدالة أثناء البناء من جانب الخادم
// يمكن أن تُستدعى مجددًا من قبل دالة لا تُنفَّذ على الخادم
// مفعّلة revalidate إن كانت إعادةالخاصية
export async function getStaticProps()
  const res = await fetch('https://.../posts')
  const posts = await res.json()
  return {
    props: {
      posts,
    },
    //إعادة تجديد الصفحة Next.js تحاول
    // Next.js will attempt to re-generate the page:
    // عندما يرد طلب
    // وغالبًا بعد 10 ثوانٍ
    revalidate: 10, // بالثواني
  }
}
</syntaxhighlight>إن دعمت الصفحة التجديد الساكن التدريجي ISR، يمكن تحديد حالة التخزين المؤقت بقراءة قيمة ترويسة الاستجابة <code>x-nextjs-cache</code> والتي قد تكون:
* <code>MISS</code>: المسار غير موجود في الذاكرة المؤقتة (يحدث ذلك غالبًا مرة واحدة عند أول زيارة).
* <code>STALE</code>: المسار موجود في الذاكرة المؤقتة لكنه تجاوز فترة الصلاحية لذا سيُحدّث في الخلفية.
* <code>HIT</code>: المسار في الذاكرة المؤقتة وضمن فترة الصلاحية.
==== الخاصية <code>notFound</code> ====
وهي قيمة منطقية تتيح للصفحة أن تعيد رمز الحالة 404 و<nowiki/>[[Next.js/custom error page|صفحة هذه الحالة]]. تُعيد الصفحة عندما تكون قيمة هذه الخاصية <code>true</code> رمز الحالة <code>404</code> حتى لو وُلِّدت الصفحة بنجاح سابقًا. إن الغاية من ذلك دعم بعض حالات الاستخدام مثل المحتوى الذي يولّده مستخدم ويُزال من قبله. تسلك الخاصية <code>notFound</code> نفسك سلوك الخاصية السابقة <code>revalidate</code>:<syntaxhighlight lang="javascript">
export async function getServerSideProps(context) {
  const res = await fetch(`https://.../data`)
  const data = await res.json()
  if (!data) {
    return {
      notFound: true,
    }
  }
  return {
    props: { data }, // يُمرر إلى مكوّن الصفحة كخاصيات
  }
}
</syntaxhighlight><blockquote>'''ملاحظة''': لا حاجة لاستخدام <code>notFound</code> مع نمط التراجع <code>fallback: false</code> لأن المسارات التي تعيدها الدالة <code>getStaticPaths</code> هي التي تُصيّر مسبقًا فقط.</blockquote>
==== الخاصية <code>redirect</code> ====
يتيح الكائن <code>redirect</code> إعادة التوجيه إلى مصادر داخلية أو خارجية، وينبغي أن يطابق الشكل <code>{ destination: string, permanent: boolean }</code>. قد تحتاج في حالات نادرة تعيين رموز حالة مخصصة لعملاء <code>HTTP</code> الأقدم كي يُعاد توجيههم بالشكل الصحيح. في حالات كهذه، استخدم الخاصية <code>statusCode</code> بدلًا من <code>permanent</code> لكن لا تستخدمهما معًا. بإمكانك أيضًا ضبط <code>basePath: false</code> بشكل مشابه لإعادة التوجيه من خلال الملف <code>next.config.js</code>:<syntaxhighlight lang="javascript">
export async function getStaticProps(context) {
  const res = await fetch(`https://...`)
  const data = await res.json()
  if (!data) {
    return {
      redirect: {
        destination: '/',
        permanent: false,
        //رمز الحالة : 301
      },
    }
  }
  return {
    props: { data }, //تمرر إلى مكوّن الصفحة كخاصيات
  }
}
</syntaxhighlight>إن كانت عمليات إعادة التوجيه معروفة أثناء بناء التطبيق، فلا بد من إضافتها إلى الملف <code>next.config.js</code> بدلًا من ذلك.
=== قراءة الملفات باستخدام <code>()process.cwd</code>: ===
بإمكانك قراءة الملفات مباشرة من منظومة الملفات في <code>getStaticProps</code>، لكن عليك أن تحصل على المسار الكامل لهذا الملف. ولن تستطيع استخدام التعليمة <code>dirname__</code> لأنها ستعيد مسارًا يختلف عن مسار المجلد، لأن Next.js تُصرّف الشيفرة في مجلدات مختلفة. استخدم بدلًا من ذلك التابع <code>()process.cwd</code> الذي يعيد المجلد الذي تُنفَّذ فيه Next.js:<syntaxhighlight lang="javascript">
import { promises as fs } from 'fs'
import path from 'path'
// getStaticProps() ستجهز المنشورات في زمن البناء عن طريق
function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li>
          <h3>{post.filename}</h3>
          <p>{post.content}</p>
        </li>
      ))}
    </ul>
  )
}
// تٌستدعى هذه الدالة أثناء البناء من جهة الخادم
// ولا تُتستدعى من جانب العميل، لذا يمكنك إنشاءاستعلام مباشر لقاعدة البياتات
export async function getStaticProps() {
  const postsDirectory = path.join(process.cwd(), 'posts')
  const filenames = await fs.readdir(postsDirectory)
  const posts = filenames.map(async (filename) => {
    const filePath = path.join(postsDirectory, filename)
    const fileContents = await fs.readFile(filePath, 'utf8')
    // ستحوّل أو تحلل هذا المحتوى عادة
    // HTML إلى markdown كأن تحوّل
    return {
      filename,
      content: fileContents,
    }
  })
  //المنشورات كخاصيات أثناء البناء Blog سيتلقى المكّون
  return {
    props: {
      posts: await Promise.all(posts),
    },
  }
}
export default Blog
</syntaxhighlight>
=== استخدام getstaticprops مع TypeScript ===
بإمكانك استخدام النوع <code>GetStaticProps</code> من المكتبة <code>next</code>:<syntaxhighlight lang="javascript">
import { GetStaticProps } from 'next'
export const getStaticProps: GetStaticProps = async (context) => {
  // ...
}
</syntaxhighlight>وإن كنت تريد استخدام الأنواع الاستدلالية inferred typings للخاصيات، بإمكانك استخدام <code><InferGetStaticPropsType<typeof getStaticProps</code>:<syntaxhighlight lang="javascript">
import { InferGetStaticPropsType } from 'next'
type Post = {
  author: string
  content: string
}
export const getStaticProps = async () => {
  const res = await fetch('https://.../posts')
  const posts: Post[] = await res.json()
  return {
    props: {
      posts,
    },
  }
}
function Blog({ posts }: InferGetStaticPropsType<typeof getStaticProps>) {
  // Post[] سيحلل البيانات وفق النوع
}
export default Blog
</syntaxhighlight>ستعمل الأنواع الضمنية أيضًا مع الدالة <code>GetStaticProps</code>:<syntaxhighlight lang="javascript">
import { InferGetStaticPropsType } from 'next'
type Post = {
  author: string
  content: string
}
export const getStaticProps = async () => {
  const res = await fetch('https://.../posts')
  const posts: Post[] = await res.json()
  return {
    props: {
      posts,
    },
  }
}
function Blog({ posts }: InferGetStaticPropsType<typeof getStaticProps>) {
  //post[] سيحلل المنشورات إلى النوع
}
export default Blog
</syntaxhighlight>
</syntaxhighlight>


سطر 100: سطر 602:


* الصفحات [https://nextjs.org/docs/api-reference/data-fetching/get-initial-props Data fetching] من توثيق Next.js الرسمي.
* الصفحات [https://nextjs.org/docs/api-reference/data-fetching/get-initial-props Data fetching] من توثيق Next.js الرسمي.
[[تصنيف:Next.js|{{SUBPAGENAME}}]]
[[تصنيف:Next.js API|{{SUBPAGENAME}}]]

المراجعة الحالية بتاريخ 16:41، 4 فبراير 2023

نستعرض في هذه الصفحة من التوثيق الدوال التي تستخدمها Next.js في إحضار البيانات.

الدالة غير المتزامنة 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 (تصيير من جانب الخادم)، تُصيَّر هذه الصفحة مسبقًا عند كل طلب باستخدام البيانات المعادة من هذه الدالة. ولهذا الأمر فائدته إن كنت تريد إحضار البيانات التي تتغير أحيانًا، وتحديث الصفحة لعرض آخر حالة للبيانات:

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

بإمكانك إدراج الوحدات البرمجية في أعلى مستوى كي تستخدمها مع getServerSideProps، ولن تُجمّع هذه الوحدات ضمن شيفرة جانب العميل (الواجهة الأمامية). أي بإمكانك كتابة الشيفرة التي تُنفّذ على الخادم مباشرة ضمن getServerSideProps بما في ذلك إحضار البيانات من قاعدة بيانات.

الكائن context

تتلقى الدالة getServerSideProps معاملًا واحدًا يدعى context، وهو كائن يمتلك المفاتيح التالية:

  • params: إن استخدمت الصفحة مسارات ديناميكية، سيضم هذا المفتاح معاملات الوجهة. إذ تبدو هذه المعاملات على الشكل { ...:id} إن كان اسم الصفحة مثلًا [id].js
  • query: قسم الاستعلام النصي من عنوان URL والمُحلَّل ككائن.
  • req: كائن الرسائل الواردة في HTTP مع الخاصية الإضافية cookies، وهي كائن له مفاتيح نصية ترتبط مع القيم النصية لملفات تعريف الارتباط.
  • res: كائن استجابة HTTP (خادم فقط).
  • preview: يأخذ القيمة true عندما تكون الصفحة في وضع الاستعراض.
  • previewData: بيانات الاستعراض التي تضبطها setPreviewData.
  • resolvedUrl: نسخة عادية من عنوان URL المطلوب بعد تجريده من بادئة next/data_ وذلك للتنقل في طرف العميل، وتتضمن قيم الاستعلام الأصلي.
  • locale: يضم الإعداد المحلي الفعّال (إن مُكِّن).
  • locales: يضم كل الإعدادات المحلية المدعومة (إن مُكِّن).
  • defaultLocale: يضم كل الإعدادات المحلية الافتراضية المهيأة (إن مُكِّن).

القيم التي تعيدها الدالة getServerSideProps

لا بد أن تعيد هذه الدالة كائنًا له إحدى الخاصيات التالية:

الخاصية props

وهو كائن من الشكل (مفتاح-قيمة) يتلقى كل قيمه من مكوّن الصفحة. ينبغي أن يكون الكائن قابلًا للتحليل كي تُحلل الخاصيات الممررة باستخدام التابع JSON.stringify.

export async function getServerSideProps(context) {
  return {
    props: { message: `Next.js is awesome` }, // تمرر إلى مكون الصفحة كخاصيات
  }
}

الخاصية notFound

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

export async function getServerSideProps(context) {
  const res = await fetch(`https://.../data`)
  const data = await res.json()

  if (!data) {
    return {
      notFound: true,
    }
  }

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

الخاصية redirect

يتيح الكائن redirect إعادة التوجيه إلى مصادر داخلية أو خارجية، وينبغي أن يطابق الشكل { destination: string, permanent: boolean }. قد تحتاج في حالات نادرة تعيين رموز حالة مخصصة لعملاء HTTP الأقدم كي يُعاد توجيههم بالشكل الصحيح. في حالات كهذه، استخدم الخاصية statusCode بدلًا من permanent لكن لا تستخدمهما معًا:

export async function getServerSideProps(context) {
  const res = await fetch(`https://.../data`)
  const data = await res.json()

  if (!data) {
    return {
      redirect: {
        destination: '/',
        permanent: false,
      },
    }
  }

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

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

بإمكانك استخدام النوع GetServerSideProps من المكتبة next:

import { GetServerSideProps } from 'next'

export const getServerSideProps: GetServerSideProps = async (context) => {
  // ...
}

وإن كنت تريد استخدام الأنواع الاستدلالية inferred typings للخاصيات، بإمكانك استخدام <InferGetServerSidePropsType<typeof getServerSideProps:

import { InferGetServerSidePropsType } from 'next'

type Data = { ... }

export const getServerSideProps = async () => {
  const res = await fetch('https://.../data')
  const data: Data = await res.json()

  return {
    props: {
      data,
    },
  }
}

function Page({ data }: InferGetServerSidePropsType<typeof getServerSideProps>) {
  // سيحلل المنشورات وفق نوع البيانات
}

export default Page

ستعمل الأنواع الضمنية أيضًا مع الدالة GetServerSideProps:

import { InferGetServerSidePropsType } from 'next'

type Data = { ... }

export const getServerSideProps = async () => {
  const res = await fetch('https://.../data')
  const data: Data = await res.json()

  return {
    props: {
      data,
    },
  }
}

function Page({ data }: InferGetServerSidePropsType<typeof getServerSideProps>) {
  // سيحلل البيانات إلى النوع المطلوب
}

export default Page

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

عندما تُصدِّر الصفحة دالة تُدعى getStaticPaths من صفحة تستخدم مسارات ديناميكية، تُصيِّر هذه الصفحة مسبقًا وبشكل ساكن كل المسارات التي تحددها الدالة.

export async function getStaticPaths() {
 return {
    paths: [
      { params: { ... } } //في الأسفل "pathes" اطلع على القسم 
    ],
    fallback: true, false or "blocking" // في الأسفل "fallback" اطلع على 
  };
}

القيم التي تعيدها الدالة getStaticPaths

لا بد أن تعيد هذه الدالة كائنًا له الخاصيات المطلوبة التالية:

الخاصية path

وهي مفتاح يحدد المسارات التي تُصيّر مسبقًا. فلو افترضنا مثلًا وجود صفحة تستخدم وجهات ديناميكية على النحو pages/posts/[id].js. فلو صَدّّرت عندها الدالة getStaticPaths من الصفحة وأعدت المسارات paths كالتالي:

return {
  paths: [
    { params: { id: '1' }},
    {
      params: { id: '2' },
      // with i18n configured the locale for the path can be returned as well
      locale: "en",
    },
  ],
  fallback: ...
}

ستوّلد Next.js بشكل ساكن الصفحات posts/1/ و posts/2/ أثناء تنفيذ الأمر next build باستخدام مكوّن الصفحة في pages/posts/[id].js.

لا بد من تطابق قيمة كل كائن params مع المعاملات المستخدمة في تسمية الصفحة:

  • إن كان اسم الصفحة pages/posts/[postId]/[commentId]، ينبغي أن يتضمن عندها الكائن params الخاصيتين postId و commentId.
  • إن استخدمت تقنية التقاط كل الوجهات في تسمية الصفحة مثل pages/[...slug]، لا بد أن يتضمن عندها الكائن params المصفوفة slug. فلو كانت هذه المصفوفة مثلًا ['hello', 'world']، ستوّلد Next.js بشكل ساكن الصفحة التي عنوانها hello/world/
  • إن استخدمت الصفحة تقنية التقاط جميع الوجهات اختياريًا، استخدم null أو [] أو undefined أو false لتصيير الوجهة الأكثر قربًا من جذر التطبيق. فلو استخدمت slug: false للصفحات pages/[[...slug]]، ستولَّد الصفحة في جذر التطبيق /.

إن المعامل params حساس لحالة الأحرف ومن الأفضل كتابته بأحرف صغيرة للتأكد من توليد المسارات بالشكل الصحيح. فلو أعيدت الكلمة WoRLD إلى معامل ما، ستطابق الحالة التي يكون فيها المسار الفعلي هو WoRLD وليس world أو World .

وبمعزل عن الكائن params، يمكن إعادة حقل الإعداد المحلي locale عندما يُهيأ التوجه i18n الذي يضبط بدوره الإعداد المحلي للمسار الذي يُولَّد.

الخاصية fallback: false

إن كانت قيمة الخاصية fallback هي false فستكون الصفحة 404 هي نتيجة كل مسار لا تعيده الدالة getStaticPaths. عندما تُنفِّذ الأمر next build، تكتشف Next.js إن أعادت الدالة getStaticPaths القيمة fallback: false، كي توّلد عندها المسارات التي تعيدها الدالة getStaticPaths فقط.

تظهر فائدة الأمر إن كان لديك عدد محدود من المسارات التي عليك إنشاؤها، أو في الحالة التي لن تضيف فيها بيانات جديدة للصفحة غالبًا. لكن إن رأيت أنك تحتاج إلى مسارات أكثر وكان fallback: false، لا بد عندها من تنفيذ الأمر next build مجددًا لكي تولَّد المسارات الجديدة.

يصيّر المثال التالي منشورًا واحدًا في الصفحة يُدعى pages/posts/[id].js. إذ تُجلب قائمة منشورات المدونة من منظومة إدارة محتوى CMS وتُعاد من قبل الدالة getStaticPaths. عندها ستحضر هذه الدالة ولكل صفحة بيانات المنشور من منظومة إدارة المحتوى باستخدام الدالة getStaticProps:

// pages/posts/[id].js

function Post({ post }) {
  // تصيير المنشور...
}

// تُستدعى هذه الدالة أثناء البناء
export async function getStaticPaths() {
  //خارجية للحصول على المنشورات API استدعي وصلة 
  const res = await fetch('https://.../posts')
  const posts = await res.json()
  // الحصول على المسارات التي تريد تصييرها مسبقًا وفقًل للمنشورات
  const paths = posts.map((post) => ({
    params: { id: post.id },
  }))
  // سنصيّر مسبقًا هذه المسارات فقط أثناء البناء
  // { fallback: false } تعيد بقية المسارات الصفحة 404 لأن
  return { paths, fallback: false }
}

// تُستدعى أيضًا أثناء البناء
export async function getStaticProps({ params }) {
  //للمنشور id المعرف  params يتضمن
  // 1 هو params.id سيكون /posts/1 في حال 
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()

  // تمرير بيانات المنشور إلى الصفحة كخاصيات
  return { props: { post } }
}

export default Post

الخاصية fallback: true

إن كانت قيمة fallback هي true سيتغير سلوك الدالة getStaticProps على النحو التالي:

  • تصيَّر المسارات المعادة من قبل الدالة getStaticProps إلى HTML أثناء البناء بواسطة الدالة getStaticProps.
  • لن ينتج عن المسارات التي لم توّلد أثناء البناء صفحات 404، بل ستقدّم نسخة تراجع عن الصفحة عند أول طلب إلى مسار كهذا. لن تُخدّم زواحف ويب Web crawler (مثل زواحف Google) بنسخة تراجع، وسيسلك المسار سلوك الحالة 'fallback: 'blocking التي سنراها تاليًا.
  • عندما ننتقل إلى صفحة فيها fallback: true من خلال الكائنين next/link أو next/router، فلن تُخدّم Next.js الطلب بصفحة تراجع بل ستسلك الصفح سلوك الحالة fallback: 'blocking'‎.
  • توّلد Next.js في الخلفية وبشكل ساكن بيانات HTML و JSON للمسار المطلوب، ويتضمن ذلك تنفيذ الدالة getStaticProps.
  • عندما تكتمل العملية، يتلقى المتصفح بيانات JSON للمسار المولّد والتي تُستخدم في التصيير التلقائي للصفحة بالخاصيات المطلوبة. ومن وجهة نظر المستخدم، ستتبدل الصفحة من صفحة تراجع إلى صفحة كاملة.
  • في الوقت ذاته، تضيف Next.js هذا المسار إلى الصفحات التي تصيَّر مسبقًا، وتُخدَّّم الطلبات اللاحقة لهذا المسار من الصفحة التي ولِّدت كغيرها من الصفحات التي تُصيّر مسبقًا أثناء البناء.

ملاحظة: لا يمكن استخدام fallback: true مع next export.

متى تستفيد من الخاصية fallback: true

تظهر فائدة fallback: true عندما يضم التطبيق عددًا كبيرًا من الصفحات الساكنة التي تعتمد على البيانات. فلو أردت تصيير جميع الصفحات في التطبيق ستسغرق عملية البناء وقتًا طويلًا جدًا.

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

لا تُحدِّث fallback: true الصفحات المولّدة، لهذا ننصحك بإلقاء نظرة على التجديد التدريجي الساكن.

الخاصية 'fallback: 'blocking

إن كانت قيمة fallback هي 'blocking'، تنتظر المسارات الجديدة التي لم تعيدها الدالة getStaticPaths حتى تولَّد صفحات HTML كما هو الحال عند التصيير في جانب الخادم، ثم تُخزّن مؤقتًا للطلبات اللاحقة أي أنها تُصيَّر مرة لكل مسار.

تسلك الدالة getStaticProps السلوك التالي:

  • تُصيَّر المسارات التي تعيدها getStaticPaths إلى صفحات HTML أثناء البناء بواسطة الدالة getStaticProps.
  • لن ينتج عن المسارات التي لم تولَّد أثناء البناء صفحات 404، بل تصييرها Next.js في جانب الخادم عند أول طلب وتعيد صفحة HTML الناتجة.
  • عندما تكتمل العملية، يتلقى المتصفح بيانات HTML للمسار المولّد. ومن وجهة نظر المستخدم، ستتبدل الحالة من "المتصفح يطلب الصفحة" إلى "تحميل الصفحة بالكامل"، ولن تكون هناك حالة تحميل أو تراجع.
  • في الوقت ذاته، تضيف Next.js هذا المسار إلى الصفحات التي تصيَّر مسبقًا، وتُخدَّّم الطلبات اللاحقة لهذا المسار من الصفحة التي ولِّدت كغيرها من الصفحات التي تُصيّر مسبقًا أثناء البناء.

لا تُحدّث 'fallback: 'blocking الصفحات المولّدة، لهذا عليك استخدام التجديد التدريجي الساكن على التوازي معها.

ملاحظة: لا يمكن استخدام 'fallback: 'blocking مع next export.

صفحات التراجع Fallback

في نسخة التراجع fallback من الصفحة:

  • ستكون خاصيات الصفحة فارغة.
  • باستخدام الموّجه، يمكنك التحقق من تصيير صفحة التراجع، إذ ستكون قيمة router.isFallback هي true.

يعرض المثال التالي حالات عن استخدام isFallback:

// pages/posts/[id].js
import { useRouter } from 'next/router'

function Post({ post }) {
  const router = useRouter()

  // إن لم تولّد الصفحة بعدن سيُعرض التالي بشكل مبدئي حتى ينتهي تنفيذ الدالة
  // getStaticProps()
  if (router.isFallback) {
    return <div>Loading...</div>
  }

  // تصيير المنشور...
}

// تُستدعى هذه الدالة أثناء البناء
export async function getStaticPaths() {
  return {
    //أثناء البناء `/posts/1` و `/posts/2` يُولّد فقط 
    paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
    // تمكين التوليد الستاتيكي لصفحات إضافية
    // مثلًا `/posts/3`
    fallback: true,
  }
}

// يُستدعى هذا أيضًا أثناء البناء
export async function getStaticProps({ params }) {
  //للمنشور id المعرف  params يتضمن
  // 1 هو params.id سيكون /posts/1 في حال 
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()

  // تمرير بيانات المنشورات إلى الصفحة عبر الخاصيات
  return {
    props: { post },
    // تجديد المنشور على الأكثر مرة كل ثانية 
    revalidate: 1,
  }
}

export default Post

استخدام getStaticPaths مع TypeScript

بإمكانك استخدام النوع GetStaticPaths من المكتبة next:

import { GetStaticPaths } from 'next'

export const getStaticPaths: GetStaticPaths = async () => {
  // ...
}

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

يؤدي تصدير دالة تُدعى getStaticProps إلى التصيير المسبق للصفحة أثناء البناء باستخدام الخاصيات التي تُعيدها الدالة.

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

بإمكانك إدراج الوحدات البرمجية في أعلى مستوى كي تستخدمها مع getStaticProps، ولن تُجمّع هذه الوحدات ضمن شيفرة جانب العميل (الواجهة الأمامية). أي بإمكانك كتابة الشيفرة التي تُنفّذ على الخادم مباشرة ضمن getStaticProps بما في ذلك إحضار البيانات من قاعدة بيانات.

الكائن context

تتلقى الدالة getStaticProps معاملًا واحدًا يدعى context، وهو كائن يمتلك المفاتيح التالية:

  • params: إن استخدمت الصفحة مسارات ديناميكية، سيضم هذا المفتاح معاملات الوجهة. إذ تبدو هذه المعاملات على الشكل { ...:id} إن كان اسم الصفحة مثلًا [id]. ينبغي أن تستخدم هذه المعاملات مع getStaticPaths، وهذا ما سنشرحه لاحقًا.
  • preview: يأخذ القيمة true عندما تكون الصفحة في وضع الاستعراض.
  • previewData: بيانات الاستعراض التي تضبطها setPreviewData.
  • locale: يضم الإعداد المحلي الفعّال (إن مُكِّن).
  • locales: يضم كل الإعدادات المحلية المدعومة (إن مُكِّن).
  • defaultLocale: يضم كل الإعدادات المحلية الافتراضية المهيأة (إن مُكِّن).

القيم التي تعيدها الدالة getStaticProps

لا بد أن تعيد هذه الدالة كائنًا له إحدى الخاصيات التالية props أو redirect أو notFound متبوعة بالخاصية الاختيارية revalidate.

الخاصية props

وهو كائن من الشكل (مفتاح-قيمة) يتلقى قيمه من مكوّن الصفحة. ينبغي أن يكون الكائن قابلًا للتحليل كي تُحلل الخاصيات الممررة باستخدام التابع JSON.stringify.

export async function getStaticProps(context) {
  return {
    props: { message: `Next.js is awesome` }, // تمرر إلى مكون الصفحة كخاصيات
  }
}

الخاصية revalidate

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

// تُستدعى هذه الدالة أثناء البناء من جانب الخادم
// يمكن أن تُستدعى مجددًا من قبل دالة لا تُنفَّذ على الخادم
// مفعّلة revalidate إن كانت إعادةالخاصية 
export async function getStaticProps() 
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  return {
    props: {
      posts,
    },
    //إعادة تجديد الصفحة Next.js تحاول
    // Next.js will attempt to re-generate the page:
    // عندما يرد طلب
    // وغالبًا بعد 10 ثوانٍ
    revalidate: 10, // بالثواني
  }
}

إن دعمت الصفحة التجديد الساكن التدريجي ISR، يمكن تحديد حالة التخزين المؤقت بقراءة قيمة ترويسة الاستجابة x-nextjs-cache والتي قد تكون:

  • MISS: المسار غير موجود في الذاكرة المؤقتة (يحدث ذلك غالبًا مرة واحدة عند أول زيارة).
  • STALE: المسار موجود في الذاكرة المؤقتة لكنه تجاوز فترة الصلاحية لذا سيُحدّث في الخلفية.
  • HIT: المسار في الذاكرة المؤقتة وضمن فترة الصلاحية.

الخاصية notFound

وهي قيمة منطقية تتيح للصفحة أن تعيد رمز الحالة 404 وصفحة هذه الحالة. تُعيد الصفحة عندما تكون قيمة هذه الخاصية true رمز الحالة 404 حتى لو وُلِّدت الصفحة بنجاح سابقًا. إن الغاية من ذلك دعم بعض حالات الاستخدام مثل المحتوى الذي يولّده مستخدم ويُزال من قبله. تسلك الخاصية notFound نفسك سلوك الخاصية السابقة revalidate:

export async function getServerSideProps(context) {
  const res = await fetch(`https://.../data`)
  const data = await res.json()

  if (!data) {
    return {
      notFound: true,
    }
  }

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

ملاحظة: لا حاجة لاستخدام notFound مع نمط التراجع fallback: false لأن المسارات التي تعيدها الدالة getStaticPaths هي التي تُصيّر مسبقًا فقط.

الخاصية redirect

يتيح الكائن redirect إعادة التوجيه إلى مصادر داخلية أو خارجية، وينبغي أن يطابق الشكل { destination: string, permanent: boolean }. قد تحتاج في حالات نادرة تعيين رموز حالة مخصصة لعملاء HTTP الأقدم كي يُعاد توجيههم بالشكل الصحيح. في حالات كهذه، استخدم الخاصية statusCode بدلًا من permanent لكن لا تستخدمهما معًا. بإمكانك أيضًا ضبط basePath: false بشكل مشابه لإعادة التوجيه من خلال الملف next.config.js:

export async function getStaticProps(context) {
  const res = await fetch(`https://...`)
  const data = await res.json()

  if (!data) {
    return {
      redirect: {
        destination: '/',
        permanent: false,
        //رمز الحالة : 301
      },
    }
  }

  return {
    props: { data }, //تمرر إلى مكوّن الصفحة كخاصيات
  }
}

إن كانت عمليات إعادة التوجيه معروفة أثناء بناء التطبيق، فلا بد من إضافتها إلى الملف next.config.js بدلًا من ذلك.

قراءة الملفات باستخدام ()process.cwd:

بإمكانك قراءة الملفات مباشرة من منظومة الملفات في getStaticProps، لكن عليك أن تحصل على المسار الكامل لهذا الملف. ولن تستطيع استخدام التعليمة dirname__ لأنها ستعيد مسارًا يختلف عن مسار المجلد، لأن Next.js تُصرّف الشيفرة في مجلدات مختلفة. استخدم بدلًا من ذلك التابع ()process.cwd الذي يعيد المجلد الذي تُنفَّذ فيه Next.js:

import { promises as fs } from 'fs'
import path from 'path'
// getStaticProps() ستجهز المنشورات في زمن البناء عن طريق 
function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li>
          <h3>{post.filename}</h3>
          <p>{post.content}</p>
        </li>
      ))}
    </ul>
  )
}
// تٌستدعى هذه الدالة أثناء البناء من جهة الخادم
// ولا تُتستدعى من جانب العميل، لذا يمكنك إنشاءاستعلام مباشر لقاعدة البياتات

export async function getStaticProps() {
  const postsDirectory = path.join(process.cwd(), 'posts')
  const filenames = await fs.readdir(postsDirectory)

  const posts = filenames.map(async (filename) => {
    const filePath = path.join(postsDirectory, filename)
    const fileContents = await fs.readFile(filePath, 'utf8')

    // ستحوّل أو تحلل هذا المحتوى عادة
    // HTML إلى markdown كأن تحوّل 

    return {
      filename,
      content: fileContents,
    }
  })
  //المنشورات كخاصيات أثناء البناء Blog سيتلقى المكّون 
  return {
    props: {
      posts: await Promise.all(posts),
    },
  }
}

export default Blog

استخدام getstaticprops مع TypeScript

بإمكانك استخدام النوع GetStaticProps من المكتبة next:

import { GetStaticProps } from 'next'

export const getStaticProps: GetStaticProps = async (context) => {
  // ...
}

وإن كنت تريد استخدام الأنواع الاستدلالية inferred typings للخاصيات، بإمكانك استخدام <InferGetStaticPropsType<typeof getStaticProps:

import { InferGetStaticPropsType } from 'next'

type Post = {
  author: string
  content: string
}

export const getStaticProps = async () => {
  const res = await fetch('https://.../posts')
  const posts: Post[] = await res.json()

  return {
    props: {
      posts,
    },
  }
}

function Blog({ posts }: InferGetStaticPropsType<typeof getStaticProps>) {
  // Post[] سيحلل البيانات وفق النوع 
}

export default Blog

ستعمل الأنواع الضمنية أيضًا مع الدالة GetStaticProps:

import { InferGetStaticPropsType } from 'next'

type Post = {
  author: string
  content: string
}

export const getStaticProps = async () => {
  const res = await fetch('https://.../posts')
  const posts: Post[] = await res.json()

  return {
    props: {
      posts,
    },
  }
}

function Blog({ posts }: InferGetStaticPropsType<typeof getStaticProps>) {
  //post[] سيحلل المنشورات إلى النوع 
}

export default Blog

المصادر

  • الصفحات Data fetching من توثيق Next.js الرسمي.