كيفية الانتقال من Create React App إلى Next.js
سيساعدك هذا الدليل على نقل موقع Create React App (CRA) الحالي إلى Next.js.
لماذا التبديل؟
هناك عدة أسباب قد تدفعك للتبديل من Create React App إلى Next.js:
بطء وقت تحميل الصفحة الأولي
يستخدم Create React App React من جانب العميل فقط. التطبيقات التي تعمل من جانب العميل فقط، والمعروفة باسم تطبيقات الصفحة الواحدة (SPAs)، غالبًا ما تعاني من بطء في وقت تحميل الصفحة الأولي. يحدث هذا لعدة أسباب:
- يحتاج المتصفح إلى انتظار تنزيل وتشغيل كود React وحزمة التطبيق بالكامل قبل أن يتمكن الكود من إرسال طلبات لتحميل البيانات.
- ينمو كود التطبيق مع كل ميزة واعتماد جديد تضيفه.
عدم وجود تقسيم تلقائي للكود
يمكن التخفيف من مشكلة بطء أوقات التحميل السابقة إلى حد ما باستخدام تقسيم الكود. ومع ذلك، إذا حاولت تقسيم الكود يدويًا، فقد تقدم عن غير قصد شلالات شبكة. يوفر Next.js تقسيمًا تلقائيًا للكود وإزالة الكود غير المستخدم مدمجًا في جهاز التوجيه وخطوة البناء الخاصة به.
شلالات الشبكة
يحدث سبب شائع لضعف الأداء عندما تقوم التطبيقات بعمل طلبات متتابعة بين العميل والخادم لجلب البيانات. أحد أنماط جلب البيانات في SPA هو عرض عنصر نائب، ثم جلب البيانات بعد تحميل المكون. لسوء الحظ، يمكن للمكون الفرعي البدء في جلب البيانات فقط بعد أن ينتهي المكون الأصلي من تحميل بياناته الخاصة، مما يؤدي إلى "شلال" من الطلبات.
بينما يتم دعم جلب البيانات من جانب العميل في Next.js، يسمح لك Next.js أيضًا بنقل جلب البيانات إلى الخادم. هذا غالبًا ما يلغي شلالات العميل-الخادم تمامًا.
حالات تحميل سريعة ومقصودة
مع دعم مدمج لـ البث عبر React Suspense، يمكنك تحديد أجزاء واجهة المستخدم التي يتم تحميلها أولًا وبأي ترتيب، دون إنشاء شلالات شبكة.
هذا يمكّنك من بناء صفحات يتم تحميلها بسرعة أكبر وتقليل تحولات التخطيط.
اختر استراتيجية جلب البيانات
اعتمادًا على احتياجاتك، يسمح لك Next.js باختيار استراتيجية جلب البيانات على مستوى الصفحة أو المكون. على سبيل المثال، يمكنك جلب البيانات من نظام إدارة المحتوى الخاص بك وعرض مشاركات المدونة في وقت البناء (SSG) لسرعة تحميل سريعة، أو جلب البيانات في وقت الطلب (SSR) عند الضرورة.
Middleware
يسمح لك Next.js Middleware بتشغيل الكود على الخادم قبل اكتمال الطلب. على سبيل المثال، يمكنك تجنب وميض المحتوى غير المصادق عليه عن طريق توجيه المستخدم إلى صفحة تسجيل الدخول في middleware للصفحات التي تتطلب مصادقة فقط. يمكنك أيضًا استخدامه لميزات مثل الاختبار A/B، والتجربة، والتدويل.
تحسينات مدمجة
غالبًا ما يكون للـ الصور، الخطوط، ونصوص الطرف الثالث تأثير كبير على أداء التطبيق. يتضمن Next.js مكونات وواجهات برمجة تطبيقات متخصصة تقوم بتحسينها تلقائيًا لك.
خطوات النقل
هدفنا هو الحصول على تطبيق Next.js يعمل في أسرع وقت ممكن حتى تتمكن بعد ذلك من تبني ميزات Next.js تدريجيًا. في البداية، سنتعامل مع تطبيقك كتطبيق يعمل من جانب العميل فقط (SPA) دون استبدال جهاز التوجيه الحالي على الفور. هذا يقلل من التعقيد والتعارضات الدمج.
ملاحظة: إذا كنت تستخدم تكوينات CRA متقدمة مثل حقل
homepage
مخصص في ملفpackage.json
الخاص بك، أو service worker مخصص، أو تعديلات محددة على Babel/webpack، يرجى الاطلاع على قسم اعتبارات إضافية في نهاية هذا الدليل للحصول على نصائح حول تكرار أو تكييف هذه الميزات في Next.js.
الخطوة 1: تثبيت تبعية Next.js
قم بتثبيت Next.js في مشروعك الحالي:
npm install next@latest
الخطوة 2: إنشاء ملف تكوين Next.js
قم بإنشاء ملف next.config.ts
في جذر مشروعك (نفس مستوى ملف package.json
الخاص بك). يحتوي هذا الملف على خيارات تكوين Next.js.
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
output: 'export', // يخرج تطبيق صفحة واحدة (SPA)
distDir: 'build', // يغير دليل إخراج البناء إلى `build`
}
export default nextConfig
ملاحظة: استخدام
output: 'export'
يعني أنك تقوم بتصدير ثابت. لن يكون لديك وصول إلى ميزات جانب الخادم مثل SSR أو واجهات برمجة التطبيقات. يمكنك إزالة هذا السطر للاستفادة من ميزات خادم Next.js.
الخطوة 3: إنشاء تخطيط الجذر
يجب أن يتضمن تطبيق App Router في Next.js ملف تخطيط جذر، وهو مكون خادم React الذي سيلف جميع صفحاتك.
أقرب ما يعادل ملف تخطيط الجذر في تطبيق CRA هو public/index.html
، والذي يتضمن علامات <html>
و<head>
و<body>
الخاصة بك.
- قم بإنشاء دليل
app
جديد داخل مجلدsrc
الخاص بك (أو في جذر مشروعك إذا كنت تفضلapp
في الجذر). - داخل دليل
app
، قم بإنشاء ملفlayout.tsx
(أوlayout.js
):
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return '...'
}
export default function RootLayout({ children }) {
return '...'
}
الآن انسخ محتوى ملف index.html
القديم في مكون <RootLayout>
هذا. استبدل body div#root
(و body noscript
) بـ <div id="root">{children}</div>
.
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<head>
<meta charSet="UTF-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>React App</title>
<meta name="description" content="Web site created..." />
</head>
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
export default function RootLayout({ children }) {
return (
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>React App</title>
<meta name="description" content="Web site created..." />
</head>
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
جيد أن تعرف: يتجاهل Next.js
public/manifest.json
الخاص بـ CRA، والرموز الإضافية، وتكوين الاختبار افتراضيًا. إذا كنت بحاجة إلى هذه، فإن Next.js يدعمها مع واجهة برمجة تطبيقات Metadata وإعداد الاختبار.
الخطوة 4: البيانات الوصفية
يتضمن Next.js تلقائيًا علامات <meta charset="UTF-8" />
و<meta name="viewport" content="width=device-width, initial-scale=1" />
، لذا يمكنك إزالتها من <head>
:
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<head>
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<title>React App</title>
<meta name="description" content="Web site created..." />
</head>
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
export default function RootLayout({ children }) {
return (
<html lang="en">
<head>
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<title>React App</title>
<meta name="description" content="Web site created..." />
</head>
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
يتم إضافة أي ملفات بيانات وصفية مثل favicon.ico
، icon.png
، robots.txt
تلقائيًا إلى علامة <head>
للتطبيق طالما قمت بوضعها في المستوى الأعلى من دليل app
. بعد نقل جميع الملفات المدعومة إلى دليل app
، يمكنك حذف علامات <link>
الخاصة بها بأمان:
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<head>
<title>React App</title>
<meta name="description" content="Web site created..." />
</head>
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
export default function RootLayout({ children }) {
return (
<html lang="en">
<head>
<title>React App</title>
<meta name="description" content="Web site created..." />
</head>
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
أخيرًا، يمكن لـ Next.js إدارة علامات <head>
الأخيرة الخاصة بك باستخدام واجهة برمجة تطبيقات Metadata. انقل معلومات البيانات الوصفية النهائية الخاصة بك إلى كائن metadata
المُصدَّر:
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'React App',
description: 'Web site created with Next.js.',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
export const metadata = {
title: 'React App',
description: 'Web site created with Next.js.',
}
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
مع التغييرات أعلاه، انتقلت من الإعلان عن كل شيء في index.html
الخاص بك إلى استخدام نهج Next.js القائم على الاصطلاح والمدمج في الإطار (واجهة برمجة تطبيقات Metadata). يمكّنك هذا النهج من تحسين تحسين محركات البحث وإمكانية مشاركة صفحاتك على الويب بسهولة أكبر.
الخطوة 5: الأنماط
مثل CRA، يدعم Next.js وحدات CSS خارج الصندوق. كما يدعم أيضًا استيراد CSS العام.
إذا كان لديك ملف CSS عام، قم باستيراده إلى app/layout.tsx
الخاص بك:
import '../index.css'
export const metadata = {
title: 'React App',
description: 'Web site created with Next.js.',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
<div id="root">{children}</div>
</body>
</html>
)
}
إذا كنت تستخدم Tailwind CSS، راجع وثائق التثبيت الخاصة بنا.
الخطوة 6: إنشاء صفحة نقطة الدخول
يستخدم Create React App src/index.tsx
(أو index.js
) كنقطة دخول. في Next.js (App Router)، كل مجلد داخل دليل app
يتوافق مع مسار، ويجب أن يحتوي كل مجلد على page.tsx
.
نظرًا لأننا نريد الاحتفاظ بالتطبيق كـ SPA الآن والاعتراض على جميع المسارات، فسنستخدم مسار catch-all اختياري.
- قم بإنشاء دليل
[[...slug]]
داخلapp
.
app
┣ [[...slug]]
┃ ┗ page.tsx
┣ layout.tsx
- أضف ما يلي إلى
page.tsx
:
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return '...' // سنقوم بتحديث هذا
}
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return '...' // سنقوم بتحديث هذا
}
يخبر هذا Next.js بإنشاء مسار واحد لـ slug الفارغ (/
)، مما يعين جميع المسارات بشكل فعال إلى نفس الصفحة. هذه الصفحة هي مكون خادم، يتم تقديمها مسبقًا إلى HTML ثابت.
الخطوة 7: إضافة نقطة دخول للعميل فقط
بعد ذلك، سنقوم بتضمين مكون App الجذر الخاص بـ CRA داخل مكون عميل بحيث يبقى كل المنطق من جانب العميل. إذا كانت هذه هي المرة الأولى التي تستخدم فيها Next.js، فمن الجدير بالذكر أن مكونات العميل (افتراضيًا) لا تزال تُقدم مسبقًا على الخادم. يمكنك التفكير فيها على أنها تمتلك القدرة الإضافية على تشغيل JavaScript من جانب العميل.
قم بإنشاء client.tsx
(أو client.js
) في app/[[...slug]]/
:
'use client'
import dynamic from 'next/dynamic'
const App = dynamic(() => import('../../App'), { ssr: false })
export function ClientOnly() {
return <App />
}
'use client'
import dynamic from 'next/dynamic'
const App = dynamic(() => import('../../App'), { ssr: false })
export function ClientOnly() {
return <App />
}
- توجيه
'use client'
يجعل هذا الملف مكون عميل. - الاستيراد الديناميكي مع
ssr: false
يعطل التقديم من جانب الخادم لمكون<App />
، مما يجعله للعميل فقط (SPA).
الآن قم بتحديث page.tsx
(أو page.js
) الخاص بك لاستخدام المكون الجديد:
import { ClientOnly } from './client'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return <ClientOnly />
}
import { ClientOnly } from './client'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return <ClientOnly />
}
الخطوة 8: تحديث استيراد الصور الثابتة
في تطبيقات CRA، يُرجع استيراد ملف صورة عنوان URL العام كسلسلة نصية:
import image from './img.png'
export default function App() {
return <img src={image} />
}
أما في Next.js، فإن استيراد الصور الثابتة يُرجع كائنًا. يمكن استخدام هذا الكائن مباشرة مع مكون <Image>
في Next.js، أو يمكنك استخدام خاصية src
من الكائن مع وسم <img>
الحالي لديك.
يتميز مكون <Image>
بفوائد تحسين الصور التلقائي. يقوم مكون <Image>
تلقائيًا بتعيين سمات width
و height
لوسم <img>
الناتج بناءً على أبعاد الصورة. هذا يمنع حدوث تغييرات في التنسيق عند تحميل الصورة. ومع ذلك، قد يسبب هذا مشاكل إذا كان تطبيقك يحتوي على صور يتم تنسيق أحد أبعادها فقط دون الآخر ليتم تعيينه إلى auto
. عندما لا يتم تعيينه إلى auto
، سيعتمد البعد على قيمة سمة البعد في وسم <img>
، مما قد يجعل الصورة تظهر مشوهة.
سيؤدي الاحتفاظ بوسم <img>
إلى تقليل عدد التغييرات في تطبيقك ومنع المشاكل المذكورة أعلاه. يمكنك بعد ذلك الانتقال لاحقًا إلى مكون <Image>
للاستفادة من تحسين الصور عن طريق تكوين محمل، أو الانتقال إلى خادم Next.js الافتراضي الذي يحتوي على تحسين تلقائي للصور.
تحويل مسارات الاستيراد المطلقة للصور المستوردة من /public
إلى استيرادات نسبية:
// قبل
import logo from '/logo.png'
// بعد
import logo from '../public/logo.png'
تمرير خاصية src
للصورة بدلاً من كائن الصورة بالكامل إلى وسم <img>
:
// قبل
<img src={logo} />
// بعد
<img src={logo.src} />
بدلاً من ذلك، يمكنك الإشارة إلى عنوان URL العام لأصل الصورة بناءً على اسم الملف. على سبيل المثال، سيتم تقديم الصورة public/logo.png
على المسار /logo.png
لتطبيقك، والذي سيكون قيمة src
.
تحذير: إذا كنت تستخدم TypeScript، فقد تواجه أخطاء نوع عند الوصول إلى خاصية
src
. لإصلاحها، تحتاج إلى إضافةnext-env.d.ts
إلى مصفوفةinclude
في ملفtsconfig.json
. سيقوم Next.js بإنشاء هذا الملف تلقائيًا عند تشغيل التطبيق في الخطوة 9.
الخطوة 9: ترحيل متغيرات البيئة
يدعم Next.js متغيرات البيئة بشكل مشابه لـ CRA ولكن يتطلب بادئة NEXT_PUBLIC_
لأي متغير تريد عرضه في المتصفح.
الفرق الرئيسي هو البادئة المستخدمة لعرض متغيرات البيئة على جانب العميل. قم بتغيير جميع متغيرات البيئة ذات البادئة REACT_APP_
إلى NEXT_PUBLIC_
.
الخطوة 10: تحديث الأوامر في package.json
قم بتحديث أوامر package.json
لاستخدام أوامر Next.js. أيضًا، أضف .next
و next-env.d.ts
إلى ملف .gitignore
:
{
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "npx serve@latest ./build"
}
}
# ...
.next
next-env.d.ts
الآن يمكنك تشغيل:
npm run dev
افتح http://localhost:3000. يجب أن ترى تطبيقك يعمل الآن على Next.js (في وضع SPA).
الخطوة 11: التنظيف
يمكنك الآن إزالة الملفات الخاصة بـ Create React App:
public/index.html
src/index.tsx
src/react-app-env.d.ts
- إعداد
reportWebVitals
- تبعية
react-scripts
(قم بإلغاء تثبيتها منpackage.json
)
اعتبارات إضافية
استخدام homepage
مخصص في CRA
إذا كنت تستخدم حقل homepage
في ملف package.json
الخاص بـ CRA لتقديم التطبيق تحت مسار فرعي معين، يمكنك تكرار ذلك في Next.js باستخدام تكوين basePath
في next.config.ts
:
import { NextConfig } from 'next'
const nextConfig: NextConfig = {
basePath: '/my-subpath',
// ...
}
export default nextConfig
التعامل مع Service Worker
مخصص
إذا كنت تستخدم service worker من CRA (مثل serviceWorker.js
من create-react-app
)، يمكنك تعلم كيفية إنشاء تطبيقات ويب تقدمية (PWAs) باستخدام Next.js.
توجيه طلبات API
إذا كان تطبيق CRA الخاص بك يستخدم حقل proxy
في package.json
لتوجيه الطلبات إلى خادم خلفي، يمكنك تكرار ذلك باستخدام إعادة الكتابة في Next.js في next.config.ts
:
import { NextConfig } from 'next'
const nextConfig: NextConfig = {
async rewrites() {
return [
{
source: '/api/:path*',
destination: 'https://your-backend.com/:path*',
},
]
},
}
تكوين Webpack / Babel مخصص
إذا كان لديك تكوين webpack أو Babel مخصص في CRA، يمكنك توسيع تكوين Next.js في next.config.ts
:
import { NextConfig } from 'next'
const nextConfig: NextConfig = {
webpack: (config, { isServer }) => {
// تعديل تكوين webpack هنا
return config
},
}
export default nextConfig
ملاحظة: هذا سيتطلب تعطيل Turbopack عن طريق إزالة
--turbopack
من أمرdev
.
إعداد TypeScript
يقوم Next.js بإعداد TypeScript تلقائيًا إذا كان لديك ملف tsconfig.json
. تأكد من إدراج next-env.d.ts
في مصفوفة include
في tsconfig.json
:
{
"include": ["next-env.d.ts", "app/**/*", "src/**/*"]
}
توافق الحزم
كل من Create React App وNext.js يستخدمان webpack افتراضيًا للحزم. يقدم Next.js أيضًا Turbopack لتطوير محلي أسرع مع:
next dev --turbopack
لا يزال بإمكانك تقديم تكوين webpack مخصص إذا كنت بحاجة إلى ترحيل إعدادات webpack المتقدمة من CRA.
الخطوات التالية
إذا نجح كل شيء، لديك الآن تطبيق Next.js يعمل كتطبيق صفحة واحدة. أنت لم تستفد بعد من ميزات Next.js مثل عرض جانب الخادم أو التوجيه القائم على الملفات، ولكن يمكنك الآن القيام بذلك تدريجيًا:
- الانتقال من React Router إلى موجه تطبيق Next.js لـ:
- تقسيم الشفرة التلقائي
- عرض الخادم المتدفق
- مكونات خادم React
- تحسين الصور باستخدام مكون
<Image>
- تحسين الخطوط باستخدام
next/font
- تحسين نصوص الطرف الثالث باستخدام مكون
<Script>
- تمكين ESLint مع قواعد Next.js الموصى بها عن طريق تشغيل
npx next lint
وتكوينه ليتناسب مع احتياجات مشروعك
ملاحظة: استخدام تصدير ثابت (
output: 'export'
) لا يدعم حاليًا خطافuseParams
أو ميزات الخادم الأخرى. لاستخدام جميع ميزات Next.js، قم بإزالةoutput: 'export'
منnext.config.ts
.