الانتقال من Create React App
سيساعدك هذا الدليل في نقل موقع Create React App الحالي إلى Next.js.
لماذا التبديل؟
هناك عدة أسباب قد تدفعك للتبديل من Create React App إلى Next.js:
بطء وقت تحميل الصفحة الأولي
يستخدم Create React App React من جانب العميل فقط. التطبيقات التي تعمل من جانب العميل فقط، والمعروفة باسم تطبيقات الصفحة الواحدة (SPAs)، غالبًا ما تعاني من بطء في وقت تحميل الصفحة الأولي. يحدث هذا لعدة أسباب:
- يحتاج المتصفح إلى انتظار تحميل وتشغيل كود React وحزمة التطبيق بالكامل قبل أن يتمكن الكود من إرسال طلبات لتحميل البيانات.
- ينمو كود التطبيق مع كل ميزة واعتماد جديد تضيفه.
عدم وجود تقسيم تلقائي للكود
يمكن إدارة مشكلة بطء أوقات التحميل السابقة إلى حد ما باستخدام تقسيم الكود. ومع ذلك، إذا حاولت إجراء تقسيم الكود يدويًا، فقد يؤدي ذلك في كثير من الأحيان إلى تفاقم الأداء. من السهل إدخال شلالات الشبكة عن طريق الخطأ عند تقسيم الكود يدويًا. يوفر Next.js تقسيم الكود التلقائي المدمج في جهاز التوجيه الخاص به.
شلالات الشبكة
يحدث سبب شائع لضعف الأداء عندما تقوم التطبيقات بعمل طلبات متتابعة بين العميل والخادم لجلب البيانات. أحد الأنماط الشائع لجلب البيانات في تطبيق SPA هو عرض عنصر نائب أولاً، ثم جلب البيانات بعد تحميل المكون. لسوء الحظ، هذا يعني أن المكون الفرعي الذي يجلب البيانات لا يمكنه البدء في الجلب حتى ينتهي المكون الأصلي من تحميل بياناته الخاصة.
بينما يتم دعم جلب البيانات من العميل باستخدام Next.js، فإنه يعطيك أيضًا خيار نقل جلب البيانات إلى الخادم، مما يمكن أن يلغي شلالات العميل-الخادم.
حالات التحميل السريعة والمقصودة
مع الدعم المدمج لـ البث عبر React Suspense، يمكنك أن تكون أكثر تحديدًا لأي أجزاء من واجهة المستخدم تريد تحميلها أولاً وبأي ترتيب دون إدخال شلالات الشبكة.
هذا يمكّنك من بناء صفحات أسرع في التحميل وتجنب تحولات التخطيط.
اختيار استراتيجية جلب البيانات
حسب احتياجاتك، يسمح لك Next.js باختيار استراتيجية جلب البيانات على أساس الصفحة والمكون. يمكنك أن تقرر الجلب في وقت البناء، أو في وقت الطلب على الخادم، أو على العميل. على سبيل المثال، يمكنك جلب البيانات من نظام إدارة المحتوى (CMS) وعرض مشاركات المدونة في وقت البناء، والتي يمكن بعد ذلك تخزينها بكفاءة على شبكة CDN.
Middleware
Next.js Middleware يسمح لك بتشغيل الكود على الخادم قبل اكتمال الطلب. هذا مفيد بشكل خاص لتجنب ظهور محتوى غير مصادق عليه عندما يزور المستخدم صفحة تتطلب مصادقة عن طريق توجيه المستخدم إلى صفحة تسجيل الدخول. كما أن Middleware مفيد للتجارب والتدويل.
تحسينات مدمجة
غالبًا ما يكون لـ الصور، الخطوط، ونصوص الطرف الثالث تأثير كبير على أداء التطبيق. يأتي Next.js مع مكونات مدمجة تقوم بتحسينها تلقائيًا لك.
خطوات النقل
هدفنا من هذا النقل هو الحصول على تطبيق Next.js يعمل في أسرع وقت ممكن، بحيث يمكنك بعد ذلك تبني ميزات Next.js تدريجيًا. في البداية، سنحافظ عليه كتطبيق يعمل من جانب العميل فقط (SPA) دون نقل جهاز التوجيه الحالي الخاص بك. هذا يساعد في تقليل فرص مواجهة مشكلات أثناء عملية النقل ويقلل من تعارضات الدمج.
الخطوة 1: تثبيت تبعية Next.js
أول شيء تحتاج إلى فعله هو تثبيت next
كتبعية:
npm install next@latest
الخطوة 2: إنشاء ملف تكوين Next.js
قم بإنشاء ملف next.config.mjs
في جذر مشروعك. سيحتوي هذا الملف على خيارات تكوين Next.js.
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export', // يخرج تطبيق صفحة واحدة (SPA).
distDir: './dist', // يغير دليل إخراج البناء إلى `./dist/`.
}
export default nextConfig
الخطوة 3: تحديث تكوين TypeScript
إذا كنت تستخدم TypeScript، فأنت بحاجة إلى تحديث ملف tsconfig.json
الخاص بك بالتغييرات التالية لجعله متوافقًا مع Next.js:
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"baseUrl": ".",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"strictNullChecks": true
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
"./dist/types/**/*.ts"
],
"exclude": ["node_modules"]
}
يمكنك العثور على مزيد من المعلومات حول تكوين TypeScript في وثائق Next.js.
الخطوة 4: إنشاء التخطيط الجذري
يجب أن يتضمن تطبيق موجه التطبيق (App Router) في Next.js ملف تخطيط جذري، وهو مكون خادم React سيضم جميع الصفحات في تطبيقك. يتم تعريف هذا الملف في المستوى الأعلى من دليل app
.
أقرب ما يعادل ملف التخطيط الجذري في تطبيق CRA هو ملف index.html
، الذي يحتوي على علامات <html>
، <head>
، و <body>
.
في هذه الخطوة، ستقوم بتحويل ملف index.html
الخاص بك إلى ملف تخطيط جذري:
- قم بإنشاء دليل
app
جديد في دليلsrc
الخاص بك. - قم بإنشاء ملف
layout.tsx
جديد داخل دليلapp
:
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return null
}
export default function RootLayout({ children }) {
return null
}
جيد أن تعرف: يمكن استخدام الامتدادات
.js
،.jsx
، أو.tsx
لملفات التخطيط.
انسخ محتوى ملف index.html
الخاص بك إلى مكون <RootLayout>
الذي تم إنشاؤه مسبقًا مع استبدال body.div#root
و body.script
بعلامة <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>
)
}
جيد أن تعرف: سنتجاهل ملف manifest، والأيقونات الإضافية بخلاف favicon، وتكوين الاختبار، ولكن إذا كانت هذه متطلبات، فإن Next.js يدعم هذه الخيارات أيضًا.
الخطوة 5: البيانات الوصفية
يتضمن Next.js بالفعل بشكل افتراضي علامات meta charset و meta viewport، لذا يمكنك إزالتها بأمان من <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 API). انقل معلومات البيانات الوصفية النهائية الخاصة بك إلى كائن 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 (واجهة برمجة تطبيقات البيانات الوصفية). يمكّنك هذا النهج من تحسين تحسين محركات البحث (SEO) وإمكانية مشاركة صفحاتك على الويب بسهولة أكبر.
الخطوة 6: الأنماط
مثل Create React App، يحتوي Next.js على دعم مدمج لـ وحدات CSS.
إذا كنت تستخدم ملف CSS عام، فقم باستيراده إلى ملف app/layout.tsx
:
import '../index.css'
// ...
إذا كنت تستخدم Tailwind، فستحتاج إلى تثبيت postcss
و autoprefixer
:
npm install postcss autoprefixer
ثم، قم بإنشاء ملف postcss.config.js
في جذر مشروعك:
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
الخطوة 7: إنشاء صفحة نقطة الدخول
في Next.js، تقوم بتعريف نقطة دخول لتطبيقك عن طريق إنشاء ملف page.tsx
. أقرب ما يعادل هذا الملف في CRA هو ملف src/index.tsx
الخاص بك. في هذه الخطوة، ستقوم بإعداد نقطة دخول التطبيق.
قم بإنشاء دليل [[...slug]]
في دليل app
الخاص بك.
نظرًا لأن هذا الدليل يهدف أولاً إلى إعداد Next.js كتطبيق SPA (تطبيق صفحة واحدة)، فأنت بحاجة إلى أن تكون نقطة دخول الصفحة قادرة على التقاط جميع المسارات الممكنة لتطبيقك. لهذا، قم بإنشاء دليل [[...slug]]
جديد في دليل app
الخاص بك.
هذا الدليل هو ما يسمى بقطاع المسار الاختياري الشامل. يستخدم Next.js جهاز توجيه قائم على نظام الملفات حيث تستخدم الأدلة لتحديد المسارات. سيضمن هذا الدليل الخاص أن جميع مسارات تطبيقك سيتم توجيهها إلى ملف page.tsx
الموجود فيه.
قم بإنشاء ملف page.tsx
جديد داخل دليل app/[[...slug]]
بالمحتوى التالي:
import '../../index.css'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return '...' // سنقوم بتحديث هذا
}
import '../../index.css'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return '...' // سنقوم بتحديث هذا
}
هذا الملف هو مكون خادم. عند تشغيل next build
، يتم تقديم الملف مسبقًا إلى أصل ثابت. لا يتطلب أي كود ديناميكي.
يستورد هذا الملف CSS العام الخاص بنا ويخبر generateStaticParams
أننا سنقوم فقط بإنشاء مسار واحد، وهو مسار الفهرس عند /
.
الآن، لننقل بقية تطبيق CRA الخاص بنا والذي سيعمل من جانب العميل فقط.
'use client'
import React from 'react'
import dynamic from 'next/dynamic'
const App = dynamic(() => import('../../App'), { ssr: false })
export function ClientOnly() {
return <App />
}
'use client'
import React from 'react'
import dynamic from 'next/dynamic'
const App = dynamic(() => import('../../App'), { ssr: false })
export function ClientOnly() {
return <App />
}
هذا الملف هو مكون عميل، معرف بتوجيه 'use client'
. لا يزال يتم تقديم مكونات العميل مسبقًا إلى HTML على الخادم قبل إرسالها إلى العميل.
نظرًا لأننا نريد بدء تطبيق يعمل من جانب العميل فقط، يمكننا تكوين Next.js لتعطيل التقديم المسبق من مكون App
وما بعده.
const App = dynamic(() => import('../../App'), { ssr: false })
الآن، قم بتحديث صفحة نقطة الدخول الخاصة بك لاستخدام المكون الجديد:
import '../../index.css'
import { ClientOnly } from './client'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return <ClientOnly />
}
import '../../index.css'
import { ClientOnly } from './client'
export function generateStaticParams() {
return [{ slug: [''] }]
}
export default function Page() {
return <ClientOnly />
}
الخطوة 8: تحديث استيراد الصور الثابتة
يتعامل Next.js مع استيراد الصور الثابتة بشكل مختلف قليلاً عن CRA. مع 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
. يمكنك تجاهل هذه الأخطاء بأمان الآن. سيتم إصلاحها بنهاية هذا الدليل.
الخطوة 9: ترحيل متغيرات البيئة
يدعم Next.js متغيرات البيئة .env
بشكل مشابه لـ CRA.
الفرق الرئيسي هو البادئة المستخدمة لعرض متغيرات البيئة على جانب العميل. قم بتغيير جميع متغيرات البيئة ذات البادئة REACT_APP_
إلى NEXT_PUBLIC_
.
الخطوة 10: تحديث الأوامر في package.json
يجب أن تكون الآن قادرًا على تشغيل تطبيقك لاختبار ما إذا كنت قد نجحت في ترحيله إلى Next.js. ولكن قبل ذلك، تحتاج إلى تحديث scripts
في package.json
بأوامر Next.js ذات الصلة، وإضافة .next
و next-env.d.ts
و dist
إلى ملف .gitignore
:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
}
}
# ...
.next
next-env.d.ts
dist
الآن قم بتشغيل npm run dev
، وافتح http://localhost:3000
. يجب أن ترى تطبيقك يعمل الآن على Next.js.
الخطوة 11: التنظيف
يمكنك الآن تنظيف قاعدة التعليمات البرمجية من آثار Create React App:
- احذف
src/index.tsx
- احذف
public/index.html
- احذف إعداد
reportWebVitals
- أزل تبعيات CRA (
react-scripts
)
توافق الحزم
يستخدم كل من Create React App وNext.js بشكل افتراضي webpack للحزم.
عند ترحيل تطبيق CRA إلى Next.js، قد يكون لديك تكوين مخصص لـ webpack تريد ترحيله. يدعم Next.js توفير تكوين مخصص لـ webpack.
علاوة على ذلك، يدعم Next.js Turbopack عبر next dev --turbo
لتحسين أداء التطوير المحلي. يدعم Turbopack بعض محملات webpack أيضًا من أجل التوافق والتبني التدريجي.
الخطوات التالية
إذا سار كل شيء وفقًا للخطة، لديك الآن تطبيق Next.js يعمل كتطبيق أحادي الصفحة. ومع ذلك، لم تستفد بعد من معظم مزايا Next.js، ولكن يمكنك الآن البدء في إجراء تغييرات تدريجية لتحقيق جميع الفوائد. إليك ما قد ترغب في القيام به بعد ذلك:
- الانتقال من React Router إلى موجه تطبيق Next.js للحصول على:
- تقسيم الشفرة تلقائيًا
- عرض من جانب الخادم مع دفق
- مكونات الخادم في React
- تحسين الصور مع مكون
<Image>
- تحسين الخطوط مع
next/font
- تحسين نصوص الطرف الثالث مع مكون
<Script>
- تحديث تكوين ESLint لدعم قواعد Next.js
معلومة مفيدة: استخدام تصدير ثابت لا يدعم حاليًا استخدام الخطاف
useParams
.