الصفحات والتخطيطات

يحتوي موجه الصفحات (Pages Router) على نظام توجيه قائم على نظام الملفات يعتمد على مفهوم الصفحات.

عند إضافة ملف إلى دليل pages، يصبح تلقائيًا متاحًا كمسار.

في Next.js، الصفحة هي مكون React يتم تصديره من ملف .js أو .jsx أو .ts أو .tsx داخل دليل pages. كل صفحة مرتبطة بمسار بناءً على اسم الملف.

مثال: إذا قمت بإنشاء pages/about.js الذي يصدر مكون React كما يلي، سيكون متاحًا على المسار /about.

export default function About() {
  return <div>About</div>
}

مسارات الفهرس

سيقوم الموجه تلقائيًا بتوجيه الملفات المسماة index إلى جذر الدليل.

  • pages/index.js/
  • pages/blog/index.js/blog

المسارات المتداخلة

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

  • pages/blog/first-post.js/blog/first-post
  • pages/dashboard/settings/username.js/dashboard/settings/username

الصفحات ذات المسارات الديناميكية

يدعم Next.js الصفحات ذات المسارات الديناميكية. على سبيل المثال، إذا قمت بإنشاء ملف باسم pages/posts/[id].js، سيكون متاحًا على posts/1، posts/2، إلخ.

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

نمط التخطيط

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

components/layout.js
import Navbar from './navbar'
import Footer from './footer'

export default function Layout({ children }) {
  return (
    <>
      <Navbar />
      <main>{children}</main>
      <Footer />
    </>
  )
}

أمثلة

تخطيط مشترك واحد مع تطبيق مخصص

إذا كان لديك تخطيط واحد لتطبيقك بالكامل، يمكنك إنشاء تطبيق مخصص وتغليف تطبيقك بالتخطيط. نظرًا لأن مكون <Layout /> يعاد استخدامه عند تغيير الصفحات، سيتم الحفاظ على حالة المكون (مثل قيم الإدخال).

pages/_app.js
import Layout from '../components/layout'

export default function MyApp({ Component, pageProps }) {
  return (
    <Layout>
      <Component {...pageProps} />
    </Layout>
  )
}

تخطيطات لكل صفحة

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

pages/index.js

import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'

export default function Page() {
  return (
    /** محتواك */
  )
}

Page.getLayout = function getLayout(page) {
  return (
    <Layout>
      <NestedLayout>{page}</NestedLayout>
    </Layout>
  )
}
pages/_app.js
export default function MyApp({ Component, pageProps }) {
  // استخدم التخطيط المحدد على مستوى الصفحة، إذا كان متاحًا
  const getLayout = Component.getLayout ?? ((page) => page)

  return getLayout(<Component {...pageProps} />)
}

عند التنقل بين الصفحات، نريد الحفاظ على حالة الصفحة (قيم الإدخال، موضع التمرير، إلخ) لتجربة تطبيق صفحة واحدة (SPA).

يمكّن نمط التخطيط هذا من استمرارية الحالة لأن شجرة مكونات React تظل محفوظة بين انتقالات الصفحات. مع شجرة المكونات، يمكن لـ React فهم العناصر التي تغيرت للحفاظ على الحالة.

معلومة جيدة: تسمى هذه العملية المصالحة، وهي كيفية فهم React للعناصر التي تغيرت.

مع TypeScript

عند استخدام TypeScript، يجب أولاً إنشاء نوع جديد لصفحاتك يتضمن دالة getLayout. ثم يجب إنشاء نوع جديد لـ AppProps يتجاوز خاصية Component لاستخدام النوع الذي تم إنشاؤه مسبقًا.

import type { ReactElement } from 'react'
import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'
import type { NextPageWithLayout } from './_app'

const Page: NextPageWithLayout = () => {
  return <p>hello world</p>
}

Page.getLayout = function getLayout(page: ReactElement) {
  return (
    <Layout>
      <NestedLayout>{page}</NestedLayout>
    </Layout>
  )
}

export default Page
import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'

const Page = () => {
  return <p>hello world</p>
}

Page.getLayout = function getLayout(page) {
  return (
    <Layout>
      <NestedLayout>{page}</NestedLayout>
    </Layout>
  )
}

export default Page
import type { ReactElement, ReactNode } from 'react'
import type { NextPage } from 'next'
import type { AppProps } from 'next/app'

export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
  getLayout?: (page: ReactElement) => ReactNode
}

type AppPropsWithLayout = AppProps & {
  Component: NextPageWithLayout
}

export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
  // استخدم التخطيط المحدد على مستوى الصفحة، إذا كان متاحًا
  const getLayout = Component.getLayout ?? ((page) => page)

  return getLayout(<Component {...pageProps} />)
}
export default function MyApp({ Component, pageProps }) {
  // استخدم التخطيط المحدد على مستوى الصفحة، إذا كان متاحًا
  const getLayout = Component.getLayout ?? ((page) => page)

  return getLayout(<Component {...pageProps} />)
}

جلب البيانات

داخل تخطيطك، يمكنك جلب البيانات على جانب العميل باستخدام useEffect أو مكتبة مثل SWR. لأن هذا الملف ليس صفحة، لا يمكنك حاليًا استخدام getStaticProps أو getServerSideProps.

components/layout.js
import useSWR from 'swr'
import Navbar from './navbar'
import Footer from './footer'

export default function Layout({ children }) {
  const { data, error } = useSWR('/api/navigation', fetcher)

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

  return (
    <>
      <Navbar links={data.links} />
      <main>{children}</main>
      <Footer />
    </>
  )
}