الفرق بين المراجعتين ل"Next.js/api data fetching"

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث
سطر 205: سطر 205:
  
 
== الدالة غير المتزامنة  <code>getStaticPaths</code> ==
 
== الدالة غير المتزامنة  <code>getStaticPaths</code> ==
 +
 +
=== القيم التي تعيدها الدالة <code>getStaticPaths</code> ===
 +
 +
==== الخاصية <code>path</code> ====
 +
 +
==== الخاصية <code>fallback: false</code> ====
 +
 +
===== الخاصية <code>fallback: true</code> =====
 +
 +
==== متى تستفيد من الخاصية <code>fallback: true</code> ====
 +
 +
==== الخاصية <code>'fallback: 'blocking</code> ====
 +
 +
=== صفحات التراجع Fallback ===
 +
 +
==== استخدام  <code>getStaticPaths</code> مع TypeScript ====
  
 
== الدالة غير المتزامنة <code>getStaticProps</code> ==
 
== الدالة غير المتزامنة <code>getStaticProps</code> ==

مراجعة 13:53، 14 يوليو 2022

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

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

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

تُمكّنك الدالة 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

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

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

الخاصية path

الخاصية fallback: false

الخاصية fallback: true

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

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

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

استخدام getStaticPaths مع TypeScript

الدالة غير المتزامنة 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

المصادر

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