Next.js هو إطار عمل لبناء تطبيقات ويب حديثة بإستخدام React. يوفر Next.js العديد من الميزات التي تساعد المطورين على بناء تطبيقات قوية وسريعة وقابلة للتطوير.
يعمل Next.js من خلال تحويل ملفات React إلى HTML ديناميكي سواء على الخادم أو أثناء البناء. يقوم بتحميل JavaScript المطلوب فقط للصفحة المحددة، مما يسرع من وقت التحميل. يدعم Next.js ثلاثة أنواع من التصيير:
توليد HTML ثابت وقت البناء. مناسب للصفحات التي لا تتغير بيانتها باستمرار.
توليد HTML في كل طلب. مناسب للصفحات التي تحتاج بيانات محدثة دائماً.
توليد HTML في المتصفح. مناسب للصفحات التفاعلية بشكل كبير.
يتم تطوير Next.js باستمرار وقد مر بالعديد من الإصدارات الرئيسية. الإصدار الحالي المستقر هو 14.x الذي يأتي مع نظام App Router الجديد ويتضمن تحسينات كبيرة في الأداء والواجهة البرمجية.
الميزة | Pages Router (التقليدي) | App Router (الجديد) |
---|---|---|
المكان | دليل /pages |
دليل /app |
التصيير | SSG, SSR, CSR | SSG, SSR, CSR بشكل أكثر تكاملاً |
مكونات الخادم | غير متوفر | متوفر افتراضياً |
تحميل البيانات | getStaticProps, getServerSideProps | fetch API داخل المكونات مباشرة |
لبدء مشروع جديد باستخدام Next.js، ستحتاج إلى Node.js مثبت على جهازك. يوفر Next.js أداة سهلة لإنشاء مشاريع جديدة.
لإنشاء مشروع جديد، قم بتنفيذ الأمر التالي في Terminal:
npx create-next-app@latest my-next-app
سيقوم هذا الأمر بإنشاء مشروع جديد ويطرح عليك بعض الأسئلة لضبط الإعدادات:
? Would you like to use TypeScript? Yes ? Would you like to use ESLint? Yes ? Would you like to use Tailwind CSS? Yes ? Would you like to use `src/` directory? No ? Would you like to use App Router? Yes ? Would you like to customize the default import alias? No
بعد إنشاء المشروع، ستجد هيكل الملفات الأساسي. فيما يلي شرح لأهم الملفات والمجلدات:
my-next-app/ ├── app/ // دليل التطبيق الرئيسي في نظام App Router │ ├── favicon.ico // أيقونة الموقع │ ├── globals.css // ملف CSS عام للتطبيق │ ├── layout.tsx // تخطيط الصفحات الرئيسي │ └── page.tsx // صفحة الرئيسية ├── public/ // ملفات الوسائط والملفات الثابتة ├── node_modules/ // حزم npm المثبتة ├── .eslintrc.json // إعدادات ESLint ├── .gitignore // قائمة الملفات التي يتم تجاهلها في Git ├── next.config.js // ملف إعدادات Next.js ├── package.json // إعدادات المشروع والتبعيات ├── postcss.config.js // إعدادات PostCSS (لـ Tailwind) ├── README.md // توثيق المشروع ├── tailwind.config.js // إعدادات Tailwind CSS └── tsconfig.json // إعدادات TypeScript
لتشغيل المشروع بوضع التطوير، انتقل إلى مجلد المشروع وقم بتنفيذ:
cd my-next-app npm run dev
سيبدأ الخادم المحلي في العمل على العنوان http://localhost:3000
، ويمكنك الآن البدء بتطوير تطبيقك.
تقوم ميزة التحديث التلقائي (Hot Reloading) في Next.js بإعادة تحميل التطبيق تلقائياً عند إجراء أي تغييرات على الكود، مما يوفر تجربة تطوير سريعة. لا حاجة لإعادة تشغيل الخادم في كل مرة تقوم فيها بتعديل الكود.
يعتمد Next.js على نظام توجيه قائم على الملفات، حيث يتم تحويل هيكل الملفات والمجلدات إلى مسارات URL تلقائياً.
في نظام App Router، توجد الصفحات والمسارات في مجلد app
. كل مجلد يمثل جزءاً من المسار، والملفات الخاصة تحدد السلوك.
app/ ├── layout.tsx // تخطيط عام ├── page.tsx // الصفحة الرئيسية "/" ├── about/ │ └── page.tsx // صفحة "عن الموقع" على "/about" ├── blog/ │ ├── layout.tsx // تخطيط خاص بقسم المدونة │ ├── page.tsx // صفحة المدونة الرئيسية "/blog" │ └── [slug]/ // متغير ديناميكي │ └── page.tsx // صفحة مقال فردي "/blog/:slug"
page.tsx
- يصبح صفحة قابلة للوصول بعنوان URLlayout.tsx
- يحدد واجهة مشتركة لعدة صفحاتloading.tsx
- يظهر أثناء تحميل المحتوىerror.tsx
- يظهر عند حدوث خطأnot-found.tsx
- يظهر عند عدم وجود صفحةroute.ts
- يحدد API خاص بهذا المسارلإنشاء صفحة جديدة، قم بإنشاء ملف page.tsx
أو page.js
في المسار المطلوب:
// app/about/page.tsx export default function AboutPage() { return ( <div className="container mx-auto py-8"> <h1 className="text-3xl font-bold">عن الموقع</h1> <p className="mt-4">هذه هي صفحة معلومات عن الموقع.</p> </div> ); }
بمجرد إنشاء هذا الملف، ستكون الصفحة متاحة على /about
.
يمكنك إنشاء مسارات ديناميكية باستخدام الأقواس المربعة:
// app/products/[id]/page.tsx export default function ProductPage({ params }) { // params.id يحتوي على قيمة المعامل من عنوان URL return ( <div> <h1>المنتج: {params.id}</h1> </div> ); }
هذا سيعمل مع مسارات مثل /products/1
، /products/shoes
إلخ.
التخطيطات تسمح بمشاركة واجهة مشتركة بين عدة صفحات:
// app/blog/layout.tsx export default function BlogLayout({ children }) { return ( <div className="blog-container"> <nav className="blog-sidebar"> <h3>أقسام المدونة</h3> <ul> <li><a href="/blog/tech">تقنية</a></li> <li><a href="/blog/design">تصميم</a></li> </ul> </nav> <main className="blog-content"> {children} </main> </div> ); }
التخطيط أعلاه سيظهر لكل الصفحات داخل مجلد /blog
.
ملفات التخطيط (layout) تحافظ على حالتها بين تنقلات الصفحات، مما يعني أنه لا يتم إعادة تحميلها مع كل تحميل لصفحة جديدة. هذا يجعل التطبيق أكثر سلاسة وأسرع.
يجب استخدام مكون Link
من Next.js للتنقل بين الصفحات بدلاً من الروابط العادية <a>
:
// مثال للتنقل بين الصفحات import Link from 'next/link'; export default function Navigation() { return ( <nav> <ul> <li> <Link href="/">الرئيسية</Link> </li> <li> <Link href="/about">عن الموقع</Link> </li> <li> <Link href={`/blog/${postId}`}>مقالة معينة</Link> </li> </ul> </nav> ); }
مكون Link
يستخدم تقنية التنقل من جانب العميل (Client-Side Navigation) مما يعني أنه لا يتم إعادة تحميل الصفحة بالكامل عند التنقل.
يعتمد Next.js على نظام المكونات الخاص بـ React، ولكنه يضيف ميزة مهمة وهي التفريق بين مكونات الخادم (Server Components) ومكونات العميل (Client Components).
في Next.js 13 وما بعده، تكون جميع المكونات افتراضياً هي مكونات خادم. هذه المكونات تنفذ على الخادم وتوفر العديد من المزايا:
مثال لمكون خادم:
// هذا مكون خادم افتراضي async function UserProfile({ userId }) { // استرجاع بيانات من قاعدة البيانات مباشرة (لا يمكن القيام بذلك في مكونات العميل) const user = await db.user.findUnique({ where: { id: userId } }); return ( <div> <h2>{user.name}</h2> <p>{user.email}</p> </div> ); }
مكونات العميل هي مكونات تنفذ في المتصفح وتكون تفاعلية. لتعريف مكون كمكون عميل، نضيف توجيه "use client"
في بداية الملف:
"use client" // هذا التوجيه يخبر Next.js أن هذا مكون عميل import { useState } from 'react'; export default function Counter() { const [count, setCount] = useState(0); return ( <div> <p>العدد الحالي: {count}</p> <button onClick={() => setCount(count + 1)}> زيادة </button> </div> ); }
استخدم مكونات العميل عندما تحتاج إلى:
يمكن دمج مكونات الخادم والعميل معاً، حيث يمكن لمكونات الخادم أن تحتوي على مكونات العميل:
// ArticlePage.js - مكون خادم import LikeButton from './LikeButton'; // مكون عميل async function getArticle(id) { const res = await fetch(`https://api.example.com/articles/${id}`); return res.json(); } export default async function ArticlePage({ params }) { const article = await getArticle(params.id); return ( <article> <h1>{article.title}</h1> <p>{article.content}</p> <LikeButton articleId={article.id} /> // استخدام مكون عميل </article> ); }
وهذا مكون العميل المشار إليه:
"use client" // LikeButton.js - مكون عميل import { useState } from 'react'; export default function LikeButton({ articleId }) { const [likes, setLikes] = useState(0); const [isLiked, setIsLiked] = useState(false); function handleLike() { if (isLiked) { setLikes(likes - 1); setIsLiked(false); } else { setLikes(likes + 1); setIsLiked(true); } // يمكن هنا إرسال طلب للخادم لتحديث عدد الإعجابات } return ( <button onClick={handleLike} className={`btn ${isLiked ? 'btn-liked' : ''}`} > {isLiked ? 'إلغاء الإعجاب' : 'إعجاب'} ({likes}) </button> ); }
يوفر Next.js عدة طرق لاسترجاع البيانات، خصوصاً في مكونات الخادم التي تدعم استخدام async/await مباشرة.
في مكونات الخادم، يمكنك استخدام fetch مباشرة لاسترجاع البيانات:
// app/users/page.tsx async function getUsers() { const res = await fetch('https://api.example.com/users'); if (!res.ok) { throw new Error('فشل في استرجاع المستخدمين'); } return res.json(); } export default async function UsersPage() { const users = await getUsers(); return ( <div> <h1>المستخدمون</h1> <ul> {users.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> </div> ); }
في Next.js، يتم تخزين طلبات fetch مؤقتًا افتراضيًا. يمكنك التحكم في سلوك التخزين المؤقت باستخدام خيارات fetch:
// استرجاع بيانات مع تخزين مؤقت (افتراضي) const staticData = await fetch('https://api.example.com/static-data'); // استرجاع بيانات جديدة في كل طلب (بدون تخزين مؤقت) const dynamicData = await fetch('https://api.example.com/dynamic-data', { cache: 'no-store' }); // استرجاع بيانات مع تخزين مؤقت لمدة 10 ثوان const revalidatedData = await fetch('https://api.example.com/data', { next: { revalidate: 10 } });
في مكونات العميل، يمكنك استخدام React Hooks مثل useEffect للاسترجاع:
"use client" import { useState, useEffect } from 'react'; export default function ClientFetching() { const [data, setData] = useState(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { async function fetchData() { try { const res = await fetch('https://api.example.com/data'); const jsonData = await res.json(); setData(jsonData); } catch (err) { setError(err.message); } finally { setIsLoading(false); } } fetchData(); }, []); if (isLoading) return <p>جاري التحميل...</p>; if (error) return <p>حدث خطأ: {error}</p>; return ( <div> <h1>البيانات</h1> <pre>{JSON.stringify(data, null, 2)}</pre> </div> ); }
SWR هي مكتبة من Vercel (الشركة المطورة لـ Next.js) تساعد في استرجاع البيانات مع إمكانيات مثل إعادة التحقق في الخلفية وتخزين البيانات مؤقتاً:
"use client" import useSWR from 'swr'; const fetcher = async (url) => { const res = await fetch(url); if (!res.ok) { throw new Error('فشل في استرجاع البيانات'); } return res.json(); }; export default function Profile() { const { data, error, isLoading } = useSWR('/api/user', fetcher); if (isLoading) return <p>جاري التحميل...</p>; if (error) return <p>حدث خطأ</p>; return ( <div> <h1>مرحباً {data.name}!</h1> </div> ); }
يوفر Next.js طريقة سهلة لإنشاء واجهات برمجة تطبيقات (API) مباشرة في المشروع، مما يسمح لك بإنشاء نقاط نهاية للتعامل مع الطلبات HTTP.
في App Router، يتم إنشاء Route Handlers في ملفات route.js
أو route.ts
:
// app/api/users/route.ts export async function GET() { const users = [ { id: 1, name: 'أحمد' }, { id: 2, name: 'محمد' }, { id: 3, name: 'سارة' }, ]; return Response.json(users); } export async function POST(request) { const data = await request.json(); // التحقق من صحة البيانات والمعالجة if (!data.name) { return Response.json( { error: 'اسم المستخدم مطلوب' }, { status: 400 } ); } // إضافة مستخدم جديد (في حالة حقيقية سيكون في قاعدة بيانات) const newUser = { id: 4, name: data.name }; return Response.json(newUser, { status: 201 }); }
يمكنك الآن الوصول إلى هذه الـ API عبر العنوان /api/users
، ويمكنك استخدام طرق HTTP المختلفة مثل GET وPOST وPUT وDELETE.
يمكنك استخدام المعلمات الديناميكية في واجهات API بنفس طريقة استخدامها في الصفحات:
// app/api/users/[id]/route.ts export async function GET(request, { params }) { const userId = params.id; // استرجاع المستخدم حسب المعرف (مثال) const user = { id: userId, name: `المستخدم ${userId}` }; return Response.json(user); } export async function DELETE(request, { params }) { const userId = params.id; // حذف المستخدم (في حالة حقيقية) // await db.user.delete({ where: { id: userId } }); return Response.json({ message: `تم حذف المستخدم ${userId} بنجاح` }); }
يمكنك استخدام ملفات API للتعامل مع المصادقة والجلسات:
// app/api/login/route.ts import { cookies } from 'next/headers'; import { sign } from 'jsonwebtoken'; export async function POST(request) { const { email, password } = await request.json(); // التحقق من بيانات المستخدم (مثال) if (email === '[email protected]' && password === 'password123') { // إنشاء توكن JWT const token = sign( { id: 1, email }, process.env.JWT_SECRET, { expiresIn: '1h' } ); // وضع التوكن في كوكيز cookies().set('token', token, { httpOnly: true, secure: process.env.NODE_ENV === 'production', maxAge: 3600, // ساعة واحدة path: '/', }); return Response.json({ success: true }); } return Response.json( { error: 'بيانات غير صحيحة' }, { status: 401 } ); }
يدعم Next.js العديد من طرق تنسيق التطبيق، بما في ذلك CSS العادي، وCSS Modules، وSCSS، وTailwind CSS، والمكتبات الأخرى.
يمكنك استيراد ملفات CSS العالمية مباشرة في ملف layout.js
الرئيسي:
// app/layout.tsx import './globals.css'; export default function RootLayout({ children, }) { return ( <html lang="ar" dir="rtl"> <body>{children}</body> </html> ); }
تسمح وحدات CSS بإنشاء أنماط محلية النطاق لتجنب تضارب الأسماء وتعزيز إعادة استخدام المكونات. يتم استخدامها عن طريق تسمية ملفات CSS بصيغة [name].module.css.
// styles/Button.module.css
.button {
padding: 0.5rem 1rem;
border-radius: 0.375rem;
font-weight: 500;
transition: all 0.2s;
}
.primary {
background-color: #FF4C29;
color: white;
}
.secondary {
background-color: #2C394B;
color: white;
}
ثم يمكنك استيراد هذه الأنماط واستخدامها في المكونات:
// components/Button.js import styles from '../styles/Button.module.css'; export default function Button({ children, variant = 'primary', onClick }) { return ( <button className={`${styles.button} ${styles[variant]}`} onClick={onClick} > {children} </button> ); }
عند تجميع التطبيق، سيقوم Next.js بتحويل أسماء الفئات إلى أسماء فريدة لضمان عدم وجود تعارض أسماء.
Tailwind CSS هو إطار عمل CSS مفيد يسمح بإنشاء تصاميم باستخدام فئات مباشرة في HTML. Next.js يوفر دعمًا مباشرًا لـ Tailwind CSS.
// تثبيت Tailwind CSS npm install -D tailwindcss postcss autoprefixer npx tailwindcss init -p
بعد التثبيت، قم بتكوين Tailwind من خلال تعديل ملف tailwind.config.js:
// tailwind.config.js module.exports = { content: [ './app/**/*.{js,ts,jsx,tsx,mdx}', './pages/**/*.{js,ts,jsx,tsx,mdx}', './components/**/*.{js,ts,jsx,tsx,mdx}', ], theme: { extend: { colors: { // إضافة ألوان مخصصة primary: '#FF4C29', secondary: '#2C394B', }, }, }, plugins: [], }
ثم يمكنك استخدام فئات Tailwind مباشرة في المكونات:
// مثال لاستخدام Tailwind CSS export default function Card({ title, description }) { return ( <div className="bg-white rounded-lg shadow-md p-6 hover:shadow-lg transition-shadow"> <h2 className="text-xl font-bold text-gray-900 mb-2">{title}</h2> <p className="text-gray-600">{description}</p> </div> ); }
يمكنك أيضًا استخدام مكتبات CSS-in-JS مثل styled-components و Emotion. عند استخدام منهج App Router، تحتاج إلى بعض الإعدادات الإضافية.
// مثال باستخدام styled-components (مع توجيه "use client") "use client" import styled from 'styled-components'; const Button = styled.button` padding: 0.5rem 1rem; border-radius: 0.375rem; font-weight: 500; background-color: ${props => props.primary ? '#FF4C29' : '#2C394B'}; color: white; transition: all 0.2s; &:hover { opacity: 0.9; } `; export default function StyledButton({ children, primary, onClick }) { return ( <Button primary={primary} onClick={onClick}> {children} </Button> ); }
يقدم Next.js العديد من التقنيات والأدوات لتحسين أداء التطبيق وتجربة المستخدم.
يوفر Next.js مكون Image المدمج لتحسين أداء الصور وتحميلها بكفاءة:
import Image from 'next/image'; export default function ProfileImage() { return ( <Image src="/images/profile.jpg" alt="صورة الملف الشخصي" width={300} height={300} priority /> ); }
مكون Image يقوم تلقائيًا بتحسين الصور من خلال:
يقوم Next.js تلقائيًا بتقسيم الكود على مستوى الصفحات والمكونات الديناميكية، ويمكنك استخدام التحميل الديناميكي للمكونات:
import dynamic from 'next/dynamic'; // استيراد المكون ديناميكيًا (يتم تحميله فقط عند الحاجة) const DynamicChart = dynamic(() => import('../components/Chart'), { loading: () => <p>جاري تحميل الرسم البياني...</p>, ssr: false, // للمكونات التي تعتمد على متصفح فقط }); export default function Dashboard() { return ( <div> <h1>لوحة التحكم</h1> <DynamicChart data={chartData} /> </div> ); }
يقدم Next.js أدوات لتحسين الخطوط وتجنب تحركات المحتوى أثناء تحميلها:
import { Inter } from 'next/font/google'; // تكوين الخط const inter = Inter({ subsets: ['latin'], display: 'swap', }); export default function RootLayout({ children }) { return ( <html lang="ar" dir="rtl" className={inter.className}> <body>{children}</body> </html> ); }
يمكنك تحسين محركات البحث (SEO) من خلال إضافة بيانات التعريف لكل صفحة:
// app/page.tsx import { Metadata } from 'next'; // تكوين بيانات التعريف للصفحة export const metadata: Metadata = { title: 'الصفحة الرئيسية | اسم المشروع', description: 'وصف الصفحة الرئيسية', openGraph: { title: 'اسم المشروع', description: 'وصف المشروع', images: ['/images/og-image.jpg'], }, }; export default function Home() { return ( <main> // محتوى الصفحة </main> ); }
بعد الانتهاء من تطوير التطبيق، يمكنك نشره على الإنترنت. توفر Vercel (الشركة خلف Next.js) أفضل تجربة نشر، ولكن يمكنك أيضًا استخدام منصات أخرى.
الطريقة الأسهل لنشر تطبيق Next.js هي استخدام منصة Vercel:
مع Vercel، يتم إعادة نشر التطبيق تلقائيًا مع كل تغيير تقوم بدفعه للمستودع.
لبناء التطبيق محليًا قبل النشر على أي منصة أخرى:
// بناء التطبيق للإنتاج npm run build // اختبار البناء محليًا npm start
يمكنك أيضًا نشر تطبيق Next.js على خدمات استضافة أخرى مثل:
عند نشر تطبيق Next.js على منصة غير Vercel، قد تحتاج إلى ضبط بعض الإعدادات الإضافية في ملف next.config.js
وإضافة ملف Dockerfile
أو ملفات تكوين أخرى حسب المنصة المستخدمة.
يعتبر Next.js إطار عمل قوي ومرن لبناء تطبيقات الويب الحديثة، ويوفر العديد من الميزات التي تجعل تجربة التطوير سلسة وممتعة.
في هذا الدليل، تعلمت كيفية:
استمر في استكشاف المزيد من ميزات Next.js واستفد من مجتمعه النشط للحصول على المساعدة والموارد.