كيفية الانتقال من صفحات (Pages) إلى موجه التطبيق (App Router)
سيساعدك هذا الدليل في:
- تحديث تطبيق Next.js من الإصدار 12 إلى الإصدار 13
- ترقية الميزات التي تعمل في كل من دليل
pages
ودليلapp
- الانتقال التدريجي لتطبيقك الحالي من
pages
إلىapp
الترقية
إصدار Node.js
أصبح الحد الأدنى لإصدار Node.js الآن v18.17. راجع توثيق Node.js لمزيد من المعلومات.
إصدار Next.js
لتحديث Next.js إلى الإصدار 13، قم بتشغيل الأمر التالي باستخدام مدير الحزم المفضل لديك:
npm install next@latest react@latest react-dom@latest
إصدار ESLint
إذا كنت تستخدم ESLint، فستحتاج إلى ترقية إصدار ESLint:
npm install -D eslint-config-next@latest
معلومة مفيدة: قد تحتاج إلى إعادة تشغيل خادم ESLint في VS Code حتى يتم تطبيق تغييرات ESLint. افتح لوحة الأوامر (
cmd+shift+p
على Mac؛ctrl+shift+p
على Windows) وابحث عنESLint: Restart ESLint Server
.
الخطوات التالية
بعد التحديث، راجع الأقسام التالية للخطوات التالية:
- ترقية الميزات الجديدة: دليل لمساعدتك في الترقية إلى ميزات جديدة مثل مكونات الصورة والرابط المحسنة.
- الانتقال من دليل
pages
إلىapp
: دليل خطوة بخطوة لمساعدتك في الانتقال التدريجي من دليلpages
إلىapp
.
ترقية الميزات الجديدة
أدخل Next.js 13 موجه التطبيق الجديد (App Router) مع ميزات واتفاقيات جديدة. يتوفر الموجه الجديد في دليل app
ويتعايش مع دليل pages
.
لا تتطلب الترقية إلى Next.js 13 استخدام موجه التطبيق. يمكنك الاستمرار في استخدام pages
مع ميزات جديدة تعمل في كلا الدليلين، مثل مكون الصورة المحدث، ومكون الرابط، ومكون النص البرمجي، وتحسين الخطوط.
مكون <Image/>
قدم Next.js 12 تحسينات جديدة لمكون الصورة باستخدام استيراد مؤقت: next/future/image
. شملت هذه التحسينات كمية أقل من JavaScript على جانب العميل، وطرق أسهل لتوسيع وتصميم الصور، وإمكانية وصول أفضل، وتحميل كسول متصفح أصلي.
في الإصدار 13، أصبح هذا السلوك الجديد هو الافتراضي لـ next/image
.
هناك اثنان من أدوات التعديل التلقائي لمساعدتك في الانتقال إلى مكون الصورة الجديد:
- أداة التعديل التلقائي
next-image-to-legacy-image
: تعيد تسمية استيراداتnext/image
إلىnext/legacy/image
بأمان وتلقائيًا. ستحافظ المكونات الحالية على نفس السلوك. - أداة التعديل التلقائي
next-image-experimental
: تضيف أنماط مضمنة بشكل خطير وتزيل الخصائص غير المستخدمة. سيغير هذا سلوك المكونات الحالية لتتناسب مع الإعدادات الافتراضية الجديدة. لاستخدام هذه الأداة، تحتاج أولاً إلى تشغيل أداةnext-image-to-legacy-image
.
مكون <Link>
لم يعد مكون <Link>
يتطلب إضافة علامة <a>
يدويًا كطفل. تمت إضافة هذا السلوك كخيار تجريبي في الإصدار 12.2 وأصبح الآن الافتراضي. في Next.js 13، يُظهر <Link>
دائمًا <a>
ويسمح لك بتمرير الخصائص إلى العلامة الأساسية.
على سبيل المثال:
import Link from 'next/link'
// Next.js 12: يجب تداخل `<a>` وإلا سيتم استبعادها
<Link href="/about">
<a>About</a>
</Link>
// Next.js 13: يُظهر `<Link>` دائمًا `<a>` في الخلفية
<Link href="/about">
About
</Link>
لترقية روابطك إلى Next.js 13، يمكنك استخدام أداة التعديل التلقائي new-link
.
مكون <Script>
تم تحديث سلوك next/script
لدعم كل من pages
وapp
، ولكن يجب إجراء بعض التغييرات لضمان انتقال سلس:
- انقل أي نصوص برمجية
beforeInteractive
قمت بتضمينها سابقًا في_document.js
إلى ملف التخطيط الجذري (app/layout.tsx
). - لا تعمل الإستراتيجية التجريبية
worker
بعد فيapp
وسيتعين إزالة النصوص البرمجية المحددة بهذه الإستراتيجية أو تعديلها لاستخدام إستراتيجية مختلفة (مثلlazyOnload
). - لن تعمل معالجات
onLoad
وonReady
وonError
في مكونات الخادم، لذا تأكد من نقلها إلى مكون العميل (Client Component) أو إزالتها تمامًا.
تحسين الخطوط
سابقًا، ساعدك Next.js في تحسين الخطوط عن طريق تضمين CSS الخطوط. يقدم الإصدار 13 وحدة next/font
الجديدة التي تمنحك القدرة على تخصيص تجربة تحميل الخطوط مع ضمان أداء وخصوصية رائعين. يعمل next/font
في كل من دليل pages
وapp
.
بينما لا يزال تضمين CSS يعمل في pages
، إلا أنه لا يعمل في app
. يجب استخدام next/font
بدلاً من ذلك.
راجع صفحة تحسين الخطوط لتعلم كيفية استخدام next/font
.
الانتقال من pages
إلى app
🎥 شاهد: تعلم كيفية اعتماد موجه التطبيق تدريجيًا → YouTube (16 دقيقة).
قد يكون الانتقال إلى موجه التطبيق أول مرة تستخدم فيها ميزات React التي يبني عليها Next.js مثل مكونات الخادم (Server Components) وSuspense والمزيد. عند دمجها مع ميزات Next.js الجديدة مثل الملفات الخاصة والتخطيطات، يعني الانتقال تعلم مفاهيم ونماذج عقلية وتغييرات سلوكية جديدة.
نوصي بتقليل التعقيد المركب لهذه التحديثات عن طريق تقسيم عملية الانتقال إلى خطوات أصغر. تم تصميم دليل app
عمدًا للعمل بالتزامن مع دليل pages
للسماح بالانتقال صفحة بصفحة تدريجيًا.
- يدعم دليل
app
المسارات المتداخلة و التخطيطات. تعلم المزيد. - استخدم المجلدات المتداخلة لتحديد المسارات وملف
page.js
الخاص لجعل جزء المسار متاحًا للجمهور. تعلم المزيد. - تُستخدم الاتفاقيات الخاصة للملفات لإنشاء واجهة مستخدم لكل جزء مسار. أكثر الملفات الخاصة شيوعًا هي
page.js
وlayout.js
.- استخدم
page.js
لتحديد واجهة مستخدم فريدة للمسار. - استخدم
layout.js
لتحديد واجهة مستخدم مشتركة عبر مسارات متعددة. - يمكن استخدام امتدادات الملفات
.js
أو.jsx
أو.tsx
للملفات الخاصة.
- استخدم
- يمكنك وضع ملفات أخرى في دليل
app
مثل المكونات والأنماط والاختبارات والمزيد. تعلم المزيد. - تم استبدال وظائف جلب البيانات مثل
getServerSideProps
وgetStaticProps
بـ واجهة برمجة تطبيقات جديدة داخلapp
. تم استبدالgetStaticPaths
بـgenerateStaticParams
. - تم استبدال
pages/_app.js
وpages/_document.js
بتخطيط جذري واحدapp/layout.js
. تعلم المزيد. - تم استبدال
pages/_error.js
بملفات خاصةerror.js
أكثر دقة. تعلم المزيد. - تم استبدال
pages/404.js
بملفnot-found.js
. - تم استبدال مسارات API
pages/api/*
بملف خاصroute.js
(Route Handler).
الخطوة 1: إنشاء دليل app
قم بتحديث إلى أحدث إصدار من Next.js (يتطلب الإصدار 13.4 أو أحدث):
npm install next@latest
ثم، أنشئ دليل app
جديد في جذر مشروعك (أو دليل src/
).
الخطوة 2: إنشاء تخطيط جذري
قم بإنشاء ملف app/layout.tsx
جديد داخل دليل app
. هذا هو التخطيط الجذري الذي سيتم تطبيقه على جميع المسارات داخل app
.
export default function RootLayout({
// يجب أن تقبل التخطيطات خاصية children.
// سيتم ملؤها بالتخطيطات أو الصفحات المتداخلة
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
export default function RootLayout({
// يجب أن تقبل التخطيطات خاصية children.
// سيتم ملؤها بالتخطيطات أو الصفحات المتداخلة
children,
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
- يجب أن يتضمن دليل
app
تخطيطًا جذريًا. - يجب أن يحدد التخطيط الجذري علامات
<html>
و<body>
لأن Next.js لا ينشئها تلقائيًا - يحل التخطيط الجذري محل ملفات
pages/_app.tsx
وpages/_document.tsx
. - يمكن استخدام امتدادات الملفات
.js
أو.jsx
أو.tsx
لملفات التخطيط.
لإدارة عناصر HTML <head>
، يمكنك استخدام دعم SEO المدمج:
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Home',
description: 'Welcome to Next.js',
}
export const metadata = {
title: 'Home',
description: 'Welcome to Next.js',
}
نقل _document.js
و_app.js
إذا كان لديك ملف _app
أو _document
موجود، يمكنك نسخ المحتويات (مثل الأنماط العامة) إلى التخطيط الجذري (app/layout.tsx
). لن تنطبق الأنماط في app/layout.tsx
على pages/*
. يجب الاحتفاظ بـ _app
/_document
أثناء الانتقال لمنع تعطل مسارات pages/*
. بمجرد اكتمال الانتقال، يمكنك حذفها بأمان.
إذا كنت تستخدم أي موفري سياق React، فسيتعين نقلهم إلى مكون عميل (Client Component).
نقل نمط getLayout()
إلى التخطيطات (اختياري)
أوصى Next.js بإضافة خاصية إلى مكونات الصفحة لتحقيق تخطيطات لكل صفحة في دليل pages
. يمكن استبدال هذا النمط بالدعم الأصلي لـ التخطيطات المتداخلة في دليل app
.
راجع مثال قبل وبعد
قبل
export default function DashboardLayout({ children }) {
return (
<div>
<h2>My Dashboard</h2>
{children}
</div>
)
}
import DashboardLayout from '../components/DashboardLayout'
export default function Page() {
return <p>My Page</p>
}
Page.getLayout = function getLayout(page) {
return <DashboardLayout>{page}</DashboardLayout>
}
بعد
-
قم بإزالة خاصية
Page.getLayout
منpages/dashboard/index.js
واتبع خطوات نقل الصفحات إلى دليلapp
.app/dashboard/page.js export default function Page() { return <p>My Page</p> }
-
انقل محتويات
DashboardLayout
إلى مكون عميل (Client Component) جديد للاحتفاظ بسلوك دليلpages
.app/dashboard/DashboardLayout.js 'use client' // يجب أن تكون هذه التوجيه في أعلى الملف، قبل أي استيرادات. // هذا مكون عميل export default function DashboardLayout({ children }) { return ( <div> <h2>My Dashboard</h2> {children} </div> ) }
-
استورد
DashboardLayout
إلى ملفlayout.js
جديد داخل دليلapp
.app/dashboard/layout.js import DashboardLayout from './DashboardLayout' // هذا مكون خادم export default function Layout({ children }) { return <DashboardLayout>{children}</DashboardLayout> }
-
يمكنك نقل أجزاء غير تفاعلية من
DashboardLayout.js
(مكون عميل) إلىlayout.js
(مكون خادم) تدريجيًا لتقليل كمية JavaScript للمكون التي ترسلها إلى العميل.
الخطوة 3: نقل next/head
في دليل pages
، يُستخدم مكون React next/head
لإدارة عناصر HTML <head>
مثل title
وmeta
. في دليل app
، تم استبدال next/head
بـ دعم SEO المدمج الجديد.
قبل:
import Head from 'next/head'
export default function Page() {
return (
<>
<Head>
<title>My page title</title>
</Head>
</>
)
}
import Head from 'next/head'
export default function Page() {
return (
<>
<Head>
<title>My page title</title>
</Head>
</>
)
}
بعد:
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'My Page Title',
}
export default function Page() {
return '...'
}
export const metadata = {
title: 'My Page Title',
}
export default function Page() {
return '...'
}
راجع جميع خيارات البيانات الوصفية.
الخطوة 4: ترحيل الصفحات
- الصفحات في دليل
app
هي مكونات خادم (Server Components) افتراضيًا. هذا يختلف عن دليلpages
حيث تكون الصفحات مكونات عميل (Client Components). - جلب البيانات (Data fetching) قد تغير في
app
. تم استبدالgetServerSideProps
وgetStaticProps
وgetInitialProps
بواجهة برمجة تطبيقات أبسط. - يستخدم دليل
app
مجلدات متداخلة لتحديد المسارات وملفpage.js
خاص لجعل جزء المسار متاحًا للجمهور. -
دليل pages
دليل app
المسار index.js
page.js
/
about.js
about/page.js
/about
blog/[slug].js
blog/[slug]/page.js
/blog/post-1
نوصي بتقسيم ترحيل الصفحة إلى خطوتين رئيسيتين:
- الخطوة 1: نقل مكون الصفحة المصدر الافتراضي إلى مكون عميل جديد.
- الخطوة 2: استيراد مكون العميل الجديد إلى ملف
page.js
جديد داخل دليلapp
.
معلومة مفيدة: هذا هو أسهل مسار للترحيل لأنه يشبه إلى حد كبير سلوك دليل
pages
.
الخطوة 1: إنشاء مكون عميل جديد
- أنشئ ملفًا جديدًا منفصلاً داخل دليل
app
(مثلapp/home-page.tsx
أو ما شابه) يقوم بتصدير مكون عميل. لتحديد مكونات العميل، أضف التوجيه'use client'
في أعلى الملف (قبل أي استيرادات).- على غرار جهاز التوجيه
Pages
، هناك خطوة تحسين لتقديم مكونات العميل مسبقًا إلى HTML ثابت عند تحميل الصفحة الأولي.
- على غرار جهاز التوجيه
- انقل مكون الصفحة المصدر الافتراضي من
pages/index.js
إلىapp/home-page.tsx
.
'use client'
// هذا مكون عميل (مشابه للمكونات في دليل `pages`)
// يستقبل البيانات كخصائص، وله إمكانية الوصول إلى الحالة والتأثيرات، ويتم
// تقديمه مسبقًا على الخادم أثناء تحميل الصفحة الأولي.
export default function HomePage({ recentPosts }) {
return (
<div>
{recentPosts.map((post) => (
<div key={post.id}>{post.title}</div>
))}
</div>
)
}
'use client'
// هذا مكون عميل. يستقبل البيانات كخصائص و
// له إمكانية الوصول إلى الحالة والتأثيرات تمامًا مثل مكونات الصفحة
// في دليل `pages`.
export default function HomePage({ recentPosts }) {
return (
<div>
{recentPosts.map((post) => (
<div key={post.id}>{post.title}</div>
))}
</div>
)
}
الخطوة 2: إنشاء صفحة جديدة
-
أنشئ ملف
app/page.tsx
جديد داخل دليلapp
. هذا مكون خادم افتراضيًا. -
استورد مكون العميل
home-page.tsx
إلى الصفحة. -
إذا كنت تقوم بجلب البيانات في
pages/index.js
، انقل منطق جلب البيانات مباشرة إلى مكون الخادم باستخدام واجهات برمجة تطبيقات جلب البيانات الجديدة. راجع دليل ترقية جلب البيانات لمزيد من التفاصيل.// استورد مكون العميل الخاص بك import HomePage from './home-page' async function getPosts() { const res = await fetch('https://...') const posts = await res.json() return posts } export default async function Page() { // جلب البيانات مباشرة في مكون خادم const recentPosts = await getPosts() // إعادة توجيه البيانات التي تم جلبها إلى مكون العميل الخاص بك return <HomePage recentPosts={recentPosts} /> }
// استورد مكون العميل الخاص بك import HomePage from './home-page' async function getPosts() { const res = await fetch('https://...') const posts = await res.json() return posts } export default async function Page() { // جلب البيانات مباشرة في مكون خادم const recentPosts = await getPosts() // إعادة توجيه البيانات التي تم جلبها إلى مكون العميل الخاص بك return <HomePage recentPosts={recentPosts} /> }
-
إذا كانت صفحتك السابقة تستخدم
useRouter
، فستحتاج إلى التحديث إلى خطافات التوجيه الجديدة. تعلم المزيد. -
ابدأ خادم التطوير الخاص بك وقم بزيارة
http://localhost:3000
. يجب أن ترى مسار الفهرس الحالي الخاص بك، الذي يتم تقديمه الآن من خلال دليلapp
.
الخطوة 5: ترحيل خطافات التوجيه
تمت إضافة جهاز توجيه جديد لدعم السلوك الجديد في دليل app
.
في app
، يجب عليك استخدام الخطافات الثلاثة الجديدة المستوردة من next/navigation
: useRouter()
، usePathname()
، و useSearchParams()
.
- خطاف
useRouter
الجديد مستورد منnext/navigation
وله سلوك مختلف عن خطافuseRouter
فيpages
الذي يتم استيراده منnext/router
.- خطاف
useRouter
المستورد منnext/router
غير مدعوم في دليلapp
ولكن يمكن الاستمرار في استخدامه في دليلpages
.
- خطاف
- خطاف
useRouter
الجديد لا يُرجع سلسلةpathname
. استخدم خطافusePathname
المنفصل بدلاً من ذلك. - خطاف
useRouter
الجديد لا يُرجع كائنquery
. معلمات البحث ومعلمات المسار الديناميكية أصبحت منفصلة الآن. استخدم خطافيuseSearchParams
وuseParams
بدلاً من ذلك. - يمكنك استخدام
useSearchParams
وusePathname
معًا للاستماع إلى تغييرات الصفحة. راجع قسم أحداث جهاز التوجيه (Router Events) لمزيد من التفاصيل. - هذه الخطافات الجديدة مدعومة فقط في مكونات العميل. لا يمكن استخدامها في مكونات الخادم.
'use client'
import { useRouter, usePathname, useSearchParams } from 'next/navigation'
export default function ExampleClientComponent() {
const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams()
// ...
}
'use client'
import { useRouter, usePathname, useSearchParams } from 'next/navigation'
export default function ExampleClientComponent() {
const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams()
// ...
}
بالإضافة إلى ذلك، يحتوي خطاف useRouter
الجديد على التغييرات التالية:
- تمت إزالة
isFallback
لأنfallback
تم استبداله. - تمت إزالة قيم
locale
وlocales
وdefaultLocales
وdomainLocales
لأن ميزات i18n المضمنة في Next.js لم تعد ضرورية في دليلapp
. تعلم المزيد حول i18n. - تمت إزالة
basePath
. البديل لن يكون جزءًا منuseRouter
. لم يتم تنفيذه بعد. - تمت إزالة
asPath
لأن مفهومas
تمت إزالته من جهاز التوجيه الجديد. - تمت إزالة
isReady
لأنه لم يعد ضروريًا. أثناء التقديم الثابت (static rendering)، أي مكون يستخدم خطافuseSearchParams()
سيتخطى خطوة التقديم المسبق وسيتم تقديمه على العميل في وقت التشغيل بدلاً من ذلك. - تمت إزالة
route
. يوفرusePathname
أوuseSelectedLayoutSegments()
بديلاً.
عرض مرجع واجهة برمجة تطبيقات useRouter()
.
مشاركة المكونات بين pages
و app
للحفاظ على توافق المكونات بين جهازي التوجيه pages
و app
، راجع خطاف useRouter
من next/compat/router
.
هذا هو خطاف useRouter
من دليل pages
، ولكنه مخصص للاستخدام أثناء مشاركة المكونات بين أجهزة التوجيه. بمجرد أن تصبح جاهزًا لاستخدامه فقط على جهاز التوجيه app
، قم بالتحديث إلى خطاف useRouter
الجديد من next/navigation
.
الخطوة 6: ترحيل طرق جلب البيانات
يستخدم دليل pages
getServerSideProps
و getStaticProps
لجلب البيانات للصفحات. داخل دليل app
، تم استبدال وظائف جلب البيانات السابقة هذه بواجهة برمجة تطبيقات أبسط مبنية على fetch()
و async
React Server Components.
export default async function Page() {
// يجب تخزين هذا الطلب مؤقتًا حتى يتم إبطاله يدويًا.
// مشابه لـ `getStaticProps`.
// `force-cache` هو الافتراضي ويمكن حذفه.
const staticData = await fetch(`https://...`, { cache: 'force-cache' })
// يجب إعادة جلب هذا الطلب في كل طلب.
// مشابه لـ `getServerSideProps`.
const dynamicData = await fetch(`https://...`, { cache: 'no-store' })
// يجب تخزين هذا الطلب مؤقتًا بعمر 10 ثوانٍ.
// مشابه لـ `getStaticProps` مع خيار `revalidate`.
const revalidatedData = await fetch(`https://...`, {
next: { revalidate: 10 },
})
return <div>...</div>
}
export default async function Page() {
// يجب تخزين هذا الطلب مؤقتًا حتى يتم إبطاله يدويًا.
// مشابه لـ `getStaticProps`.
// `force-cache` هو الافتراضي ويمكن حذفه.
const staticData = await fetch(`https://...`, { cache: 'force-cache' })
// يجب إعادة جلب هذا الطلب في كل طلب.
// مشابه لـ `getServerSideProps`.
const dynamicData = await fetch(`https://...`, { cache: 'no-store' })
// يجب تخزين هذا الطلب مؤقتًا بعمر 10 ثوانٍ.
// مشابه لـ `getStaticProps` مع خيار `revalidate`.
const revalidatedData = await fetch(`https://...`, {
next: { revalidate: 10 },
})
return <div>...</div>
}
التقديم من جانب الخادم (getServerSideProps
)
في دليل pages
، يُستخدم getServerSideProps
لجلب البيانات على الخادم وإعادة توجيه الخصائص إلى مكون React المصدر الافتراضي في الملف. يتم تقديم HTML الأولي للصفحة من الخادم، يليه "ترطيب" الصفحة في المتصفح (جعلها تفاعلية).
// دليل `pages`
export async function getServerSideProps() {
const res = await fetch(`https://...`)
const projects = await res.json()
return { props: { projects } }
}
export default function Dashboard({ projects }) {
return (
<ul>
{projects.map((project) => (
<li key={project.id}>{project.name}</li>
))}
</ul>
)
}
في جهاز التوجيه app
، يمكننا وضع جلب البيانات داخل مكونات React باستخدام مكونات الخادم (Server Components). هذا يسمح لنا بإرسال كمية أقل من JavaScript إلى العميل، مع الحفاظ على HTML المقدم من الخادم.
عن طريق تعيين خيار cache
إلى no-store
، يمكننا الإشارة إلى أن البيانات التي تم جلبها يجب ألا يتم تخزينها مؤقتًا أبدًا. هذا مشابه لـ getServerSideProps
في دليل pages
.
// دليل `app`
// يمكن تسمية هذه الوظيفة بأي شيء
async function getProjects() {
const res = await fetch(`https://...`, { cache: 'no-store' })
const projects = await res.json()
return projects
}
export default async function Dashboard() {
const projects = await getProjects()
return (
<ul>
{projects.map((project) => (
<li key={project.id}>{project.name}</li>
))}
</ul>
)
}
// دليل `app`
// يمكن تسمية هذه الوظيفة بأي شيء
async function getProjects() {
const res = await fetch(`https://...`, { cache: 'no-store' })
const projects = await res.json()
return projects
}
export default async function Dashboard() {
const projects = await getProjects()
return (
<ul>
{projects.map((project) => (
<li key={project.id}>{project.name}</li>
))}
</ul>
)
}
الوصول إلى كائن الطلب
في دليل pages
، يمكنك استرداد بيانات الطلب بناءً على واجهة برمجة تطبيقات HTTP لـ Node.js.
على سبيل المثال، يمكنك استرداد كائن req
من getServerSideProps
واستخدامه لاسترداد ملفات تعريف الارتباط (cookies) ورؤوس الطلب.
// دليل `pages`
export async function getServerSideProps({ req, query }) {
const authHeader = req.getHeaders()['authorization'];
const theme = req.cookies['theme'];
return { props: { ... }}
}
export default function Page(props) {
return ...
}
يكشف دليل app
وظائف جديدة للقراءة فقط لاسترداد بيانات الطلب:
headers
: يعتمد على واجهة برمجة تطبيقات رؤوس الويب، ويمكن استخدامه داخل مكونات الخادم (Server Components) لاسترداد رؤوس الطلب.cookies
: يعتمد على واجهة برمجة تطبيقات ملفات تعريف الارتباط للويب، ويمكن استخدامه داخل مكونات الخادم (Server Components) لاسترداد ملفات تعريف الارتباط.
// دليل `app`
import { cookies, headers } from 'next/headers'
async function getData() {
const authHeader = (await headers()).get('authorization')
return '...'
}
export default async function Page() {
// يمكنك استخدام `cookies` أو `headers` داخل مكونات الخادم
// مباشرة أو في وظيفة جلب البيانات الخاصة بك
const theme = (await cookies()).get('theme')
const data = await getData()
return '...'
}
// دليل `app`
import { cookies, headers } from 'next/headers'
async function getData() {
const authHeader = (await headers()).get('authorization')
return '...'
}
export default async function Page() {
// يمكنك استخدام `cookies` أو `headers` داخل مكونات الخادم
// مباشرة أو في وظيفة جلب البيانات الخاصة بك
const theme = (await cookies()).get('theme')
const data = await getData()
return '...'
}
توليد الموقع الثابت (getStaticProps
)
في دليل pages
، تُستخدم وظيفة getStaticProps
لتقديم صفحة مسبقًا في وقت البناء. يمكن استخدام هذه الوظيفة لجلب البيانات من واجهة برمجة تطبيقات خارجية أو مباشرة من قاعدة بيانات، وإعادة توجيه هذه البيانات إلى الصفحة بأكملها أثناء إنشائها أثناء البناء.
// دليل `pages`
export async function getStaticProps() {
const res = await fetch(`https://...`)
const projects = await res.json()
return { props: { projects } }
}
export default function Index({ projects }) {
return projects.map((project) => <div>{project.name}</div>)
}
في دليل app
، سيكون جلب البيانات باستخدام fetch()
افتراضيًا إلى cache: 'force-cache'
، والذي سيخزن بيانات الطلب مؤقتًا حتى يتم إبطالها يدويًا. هذا مشابه لـ getStaticProps
في دليل pages
.
// دليل `app`
// يمكن تسمية هذه الوظيفة بأي شيء
async function getProjects() {
const res = await fetch(`https://...`)
const projects = await res.json()
return projects
}
export default async function Index() {
const projects = await getProjects()
return projects.map((project) => <div>{project.name}</div>)
}
المسارات الديناميكية (getStaticPaths
)
في دليل pages
، تُستخدم دالة getStaticPaths
لتحديد المسارات الديناميكية التي يجب تقديمها مسبقًا أثناء وقت البناء.
// دليل `pages`
import PostLayout from '@/components/post-layout'
export async function getStaticPaths() {
return {
paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
}
}
export async function getStaticProps({ params }) {
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
return { props: { post } }
}
export default function Post({ post }) {
return <PostLayout post={post} />
}
في دليل app
، يتم استبدال getStaticPaths
بـ generateStaticParams
.
تتصرف generateStaticParams
بشكل مشابه لـ getStaticPaths
، ولكن لديها واجهة برمجة تطبيقات مبسطة لإعادة معلمات المسار ويمكن استخدامها داخل التنسيقات (layouts). الشكل الذي تُعيده generateStaticParams
هو مصفوفة من المقاطع بدلاً من مصفوفة من كائنات param
متداخلة أو سلسلة من المسارات المحلولة.
// دليل `app`
import PostLayout from '@/components/post-layout'
export async function generateStaticParams() {
return [{ id: '1' }, { id: '2' }]
}
async function getPost(params) {
const res = await fetch(`https://.../posts/${(await params).id}`)
const post = await res.json()
return post
}
export default async function Post({ params }) {
const post = await getPost(params)
return <PostLayout post={post} />
}
استخدام الاسم generateStaticParams
أكثر ملاءمة من getStaticPaths
للنموذج الجديد في دليل app
. تم استبدال البادئة get
بـ generate
الأكثر وصفًا، والتي تتناسب بشكل أفضل الآن بعد أن لم تعد getStaticProps
و getServerSideProps
ضرورية. تم استبدال اللاحقة Paths
بـ Params
، وهي أكثر ملاءمة للتوجيه المتداخل مع مقاطع ديناميكية متعددة.
استبدال fallback
في دليل pages
، تُستخدم الخاصية fallback
التي تُرجعها getStaticPaths
لتحديد سلوك الصفحة التي لم يتم تقديمها مسبقًا أثناء وقت البناء. يمكن تعيين هذه الخاصية إلى true
لعرض صفحة احتياطية أثناء إنشاء الصفحة، أو false
لعرض صفحة 404، أو blocking
لإنشاء الصفحة عند وقت الطلب.
// دليل `pages`
export async function getStaticPaths() {
return {
paths: [],
fallback: 'blocking'
};
}
export async function getStaticProps({ params }) {
...
}
export default function Post({ post }) {
return ...
}
في دليل app
، تتحكم خاصية config.dynamicParams
في كيفية التعامل مع المعلمات خارج generateStaticParams
:
true
: (الافتراضي) يتم إنشاء المقاطع الديناميكية غير المضمنة فيgenerateStaticParams
عند الطلب.false
: ستعيد المقاطع الديناميكية غير المضمنة فيgenerateStaticParams
خطأ 404.
هذا يحل محل خيار fallback: true | false | 'blocking'
لـ getStaticPaths
في دليل pages
. لم يتم تضمين خيار fallback: 'blocking'
في dynamicParams
لأن الفرق بين 'blocking'
و true
ضئيل مع البث (streaming).
// دليل `app`
export const dynamicParams = true;
export async function generateStaticParams() {
return [...]
}
async function getPost(params) {
...
}
export default async function Post({ params }) {
const post = await getPost(params);
return ...
}
عند تعيين dynamicParams
إلى true
(الافتراضي)، عند طلب مقطع مسار لم يتم إنشاؤه، سيتم عرضه من جانب الخادم (server-rendered) وتخزينه مؤقتًا.
التجديد الثابت التدريجي (getStaticProps
مع revalidate
)
في دليل pages
، تسمح لك دالة getStaticProps
بإضافة حقل revalidate
لإعادة إنشاء الصفحة تلقائيًا بعد فترة زمنية محددة.
// دليل `pages`
export async function getStaticProps() {
const res = await fetch(`https://.../posts`)
const posts = await res.json()
return {
props: { posts },
revalidate: 60,
}
}
export default function Index({ posts }) {
return (
<Layout>
<PostList posts={posts} />
</Layout>
)
}
في دليل app
، يمكن لجلب البيانات باستخدام fetch()
استخدام revalidate
، والتي ستخزن الطلب مؤقتًا للعدد المحدد من الثواني.
// دليل `app`
async function getPosts() {
const res = await fetch(`https://.../posts`, { next: { revalidate: 60 } })
const data = await res.json()
return data.posts
}
export default async function PostList() {
const posts = await getPosts()
return posts.map((post) => <div>{post.name}</div>)
}
مسارات API
تستمر مسارات API في العمل في دليل pages/api
دون أي تغييرات. ومع ذلك، تم استبدالها بـ معالجات المسار (Route Handlers) في دليل app
.
تسمح لك معالجات المسار بإنشاء معالجات طلب مخصصة لمسار معين باستخدام واجهات برمجة تطبيقات الويب Request و Response.
export async function GET(request: Request) {}
export async function GET(request) {}
معلومة جيدة: إذا كنت تستخدم مسارات API سابقًا لاستدعاء API خارجي من العميل، يمكنك الآن استخدام مكونات الخادم (Server Components) بدلاً من ذلك لجلب البيانات بأمان. تعلم المزيد عن جلب البيانات.
تطبيقات الصفحة الواحدة (SPA)
إذا كنت تقوم أيضًا بالانتقال إلى Next.js من تطبيق صفحة واحدة (SPA) في نفس الوقت، راجع توثيقنا لمعرفة المزيد.
الخطوة 7: التنسيق
في دليل pages
، تقتصر أوراق الأنماط العامة (global stylesheets) على pages/_app.js
فقط. مع دليل app
، تم رفع هذا القيد. يمكن إضافة الأنماط العامة إلى أي تنسيق (layout) أو صفحة أو مكون.
Tailwind CSS
إذا كنت تستخدم Tailwind CSS، فستحتاج إلى إضافة دليل app
إلى ملف tailwind.config.js
الخاص بك:
module.exports = {
content: [
'./app/**/*.{js,ts,jsx,tsx,mdx}', // <-- أضف هذا السطر
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
],
}
ستحتاج أيضًا إلى استيراد الأنماط العامة في ملف app/layout.js
الخاص بك:
import '../styles/globals.css'
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
تعلم المزيد عن التنسيق باستخدام Tailwind CSS
استخدام موجه التطبيق (App Router) مع موجه الصفحات (Pages Router)
عند التنقل بين المسارات التي يقدمها موجهي Next.js المختلفين، سيكون هناك تنقل صعب (hard navigation). لن يقوم الربط التلقائي المسبق مع next/link
بالجلب المسبق عبر الموجهين.
بدلاً من ذلك، يمكنك تحسين التنقلات بين موجه التطبيق وموجه الصفحات للاحتفاظ بالجلب المسبق والانتقال السريع بين الصفحات. تعلم المزيد.
Codemods
يوفر Next.js تحويلات Codemod للمساعدة في ترقية قاعدة التعليمات البرمجية عند إهمال ميزة ما. راجع Codemods لمزيد من المعلومات.