برمجة

تعلم Next.js بالعربية - إضافة صفحات متعددة وربطها عبر Link
 والمُوجّه (Router)

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

السلام عليكم ورحمة الله، أسعد الله صباحكم والمساء أحبتي الكرام.

قبيل شهر رمضان الكريم كتبت على حسابي بتويتر ولينكدإن أنني سأبدء نشر سلسلة مترجمة إلى العربية لإطار عمل Next.js التي عرفتها أكثر من سنة من الإستخدام وتطوير المشاريع الرقمية المختلفة. جلعتني أنتقل من بيئة رياكت الكلاسيكية إلى بيئة عمل مساعدة مختصرة للوقت والجهد، لست مضطراً مثلما كنت بالسابق إلى تجهيز بيئة التطوير يدوياً (create-react-app) وتنصيب مئات المكتبات الأخرى عدا مئات الأسطر لتعريف صفحات متداخلة ..إلى آخره من الشفرات المكررة في كل مشروع.

بعدما رأيت فوائدها على إنتاجيتي فكرت في كتابة مقال يتحدث عنها، ثم ما لبثت أن قررت ترجمة كتّيب حولها لتبقى مرجعاً للأمة العربية والإسلامية مثلما عملت مع مكتبة Alpine.js.

هذا الكتيّب يحوي 120 صفحة بعد ترخيص من كاتبها فلافيُو كوبس. كان قد شرحها مطلع سنة 2019 بنسخة Next.js إصدار 9 القديم. والآن وصل إصدار الإطار العمل إلى النسخة 10+ وقد ألغيت العديد من الخصائص والأدوات. فعمدت إلى تنقيحها وتحديثها من جديد كما عمدت إلى تعريف وشرح مصطلحات إضافية مثل "built-in" مثلا لم يشرحها الكاتب لمعرفتي بعدم توفّرها باللغة العربية.

يحوي الفهرس على 28 درس مختلف، كل درس يشرح ويناقش خصائص ومزايا معيّنة. هدف هذه السلسلة هي أخذ لفّة شاملة وافية حول الإطار ثم بعدها تكمل طريق تعلمك مباشرة من التوثيق للموقع الرسمي للإطار. وبإذن الله ستختصر عليك رحلة تعلّمها بشكل مفيد جداً.

بعد الإنتهاء منها سأجمعها في كتاب واحد بصيغة PDF وأنشرها كملفات ماركداون مفتوحة المصدر على صفحتي بـ Github ليأتي من بعدي من يقوم بتحديثها بمرور الوقت إن شاء الله.

أنوّه إلى من قام بمساعدتي الأخ الفاضل عاطف بن علي من دولة تونس الشقيقة مبرمج ومطوّر ولديه فصاحة وتدقيق لغوي جيدّ سبق وأن ساعدني على تنقيح ترجمة توثيق Alpine.js كاملة. قد بدء العمل معي في الصفحات الأولى لهذا الكتيّب ثم لمشاغل لديه لم نكمل وسيعود باذن الله تعالى عن قريب.أسال الله له التوفيق والسلامة والعافية في الدنيا والآخرة.

نماذج عربية

قمت ببناء مشاريع عربية كثيرة باستخدام Next.js منها محرّر دوّن الذي بينته عبر سلسلة تحدي بناء محرّر نصوص ماركداون عربي وهناك نماذج أخرى مثل مشروع معجمي التابع لشركة صخر سبق وأن كتبت عنه بـ مجتمع حسوب وغيره المعجم المعاصر.


ملاحظة: إذا واجهت أي خطأ لغوي أو إملائي سعيد أن تنبّهني عليه في قسم التعليقات بالأسفل جزيت خيراً.

الفهرس

  1. المقدّمة.
  2. مدخل إلى Next.js.
  3. الميزات الرئيسة التي تقدّمها Next.js.
  4. الفرق بين Next.js وGatsby وcreate-react-app.
  5. تنصيب Next.js.
  6. معاينة الشفرة المصدرية للتحقق من عمل SSR.
  7. حُزم التطبيق (The app bundles).
  8. إلام يعني الرمز الذي يظهر أسفل يمين الصفحة؟
  9. تنصيب إضافة (React DevTools).
  10. تقنيات مختلفة يمكنك استعمالها لتنقيح الأخطاء البرمجية (Debugging Techniques).
  11. إضافة صفحات ثانية للمشروع.
  12. ربط صفحتين مع بعض.
  13. المحتوى التفاعلي (Dynamic content) مع المُوجّه (Router).
  14. التحضير المُسبق (Prefetching).
  15. استخدام المُوجّه (Router) لتحديد الصفحة النَشِطة (Active Link).
  16. استخدام next/router.
  17. استعلام البيانات عبر getInitialProps.
  18. التنسيق بـ CSS.
  19. استخدام وُسوم مخصّصة داخل Head.
  20. تصميم وتغليف مكوّنات الصفحة (Wrapper Component).
  21. استعمال API Route.
  22. تشغيل التطبيق من جهة المستخدم (Client Side) أو من جهة الخادوم (Server Side).
  23. نشر الإصدار النهائي.
  24. رفع التطبيق على Vercel.
  25. تحليل حُزم التطبيق (The App Bundles).
  26. التحميل البطيء (Lazy loading) للوحدات.
  27. دليل مسارك القادم.

المقدّمة

العمل بالمكتبات الجديدة مثل React أو أيّ مكتبات ثانية مدعومة بها أمر رائع، لكن تلك المكتبات لا تخلو من المشاكل المتعلّقة بعرض البيانات Rendering والمحتوى من جهة المستخدم.

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

ثانيًا، إذا كنت تقوم بإنشاء موقع ويب مُتاح للجمهور، فستواجه مشكلة في تحسين أرشفة محركات البحث SEO لصفحات موقعك. صحيح أن محركات البحث تشغّل وتقرأ شفرات JavaScript لاستخلاص البيانات منها، ولكن من الأفضل أن نرسل لهم المحتوى بدلاً من السماح لهم بذلك.

الحل لكل من هاتين المشكلتين هو العرض من جهة الخادوم (Server Rendering) أو اختصارا SSR، ويسمى أيضًا العرض المسبق الثابت (Static Pre-Rendering) .

Next.js هو واحد من أطر React التي تقوم بكل هذا بطريقة سهلة، ولا يقتصر على هذا فحسب بل لإنشاء تطبيق عليها لا تحتاج إلى أيّ تكوّين مسبق zero-configuration وبأمر واحد فقط single-command toolchain.

يوفر Next.js بنية مشتركة تسمح لك ببناء تطبيق React لواجهة تطبيقك الأمامية بسهولة، ويتعامل بشفافية مع العرض من جانب الخادوم نيابة عنك.

معلومة: سلسلة الأدوات (tool-chain) تعني مجموعة أدوات تستخدم لأداء مهمّة بناء وتطوير المشاريع البرمجية.

الميزات الرئيسة التي يقدّمها Next.js

فيما يلي قائمة غير شاملة لكل ميزات Next.js وسأسرد لك الرئيسة منها:

1. إعادة التحميل الفوري للتغييرات (Hot Code Reloading):

يقوم Next.js بإعادة تحميل الصفحة عندما يكتشف أي تغيير محفوظ حديثا.

2. التوجيه التلقائي (Automatic Routing):

أي رابط URL يتم تضمينه بنظام الملفات الموضوعة داخل pages لا تحتاج إلى أي تعريف أو تكوين.

3. مكونات فردية (Single File Components):

باستخدام styled-jsx -المتكامل مع Next.js تمامًا، والذي تم إنشاؤه من نفس الفريق- يَسهّل عليك إضافة أنماط محددة لكل مكوّن على حدة.

4. العرض من جهة الخادوم(SSR أو Server Rendering):

يمكنك تجهيز المحتوى وعرض المكوّنات قبل إرسال HTML إلى المستخدم.

توافق النظام البيئي (Ecosystem Compatibility):

يتوافق Next.js بشكل جيد مع باقي مكتبات وأنظمة JavaScript وNode وReact.

5. التقسيم التلقائي للشفرة البرمجية (Automatic Code Splitting):

يتم تحميل الصفحات التي يحتاجها الجافاسكربت فقط، فلا يقوم بتحميل كل شفرات الجافاسكربت في ملف واحد. يتولى Next.js التقسيم broken up تلقائياً لعدة مصادر مختلفة.

يفعل Next.js ذلك من خلال تحليل الوحدات المستوردة resources imported، على سبيل المثال لو قمت باستدعاء مكتبة Axios في صفحة واحدة من صفحات تطبيقك، فلا يتم تحميلها في كل الصفحات الثانية، بل يتم استدعاؤها لتلك الصفحة فقط.

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

هناك استثناء واحد ملحوظ: في كثير من الأحيان يتم استخدام imports بشكل متكرر في حزمة JavaScript الرئيسة، إذا تم استخدامها بكثرة ستستدعى في نصف صفحات الموقع على الأقل.

6. التحضير المُسبق (Prefetching):

في الربط بين المكوّنات بـ Link -التي تستخدم لربط الصفحات ببعضها- تدعم التحضير المسبق prefetch الذي يقوم تلقائياً بإعداد موارد الصفحة مسبقاً بما في ذلك تضمين الشفرة المفقودة بسبب التقسيم التلقائي للشفرة البرمجية .

7. المحتوى التفاعلي (Dynamic content):

يمكنك استيراد وحدات الجافاسكربت ومكونات React ديناميكيًا.

8. التصدير الساكن -الثابت- (Static Exports):

باستخدام الأمر next export، يتيح لك Next.js تصدير موقع ثابت بالكامل من تطبيقك.

9. دعم TypeScript

تمت كتابة Next.js بلغة TypeScript وبهذا يأتي مع دعم TypeScript ممتاز.

الفرق بين Next.js وGatsby وcreate-react-app

Next.js وGatsby، هما أداتان قويتان يمكننا استخدامها لبناء مشاريعنا، والقاسم المشترك بينهما أنهما تعملان تحت غطاء React. ويأتيان مجردان من مكتبة webpack وكل تلك الأدوات low-level التي كنا نجهّزها يدوياً في السابق.

create-react-app لا يساعدك في إنشاء تطبيق يتم عرضه من جانب الخادوم بسهولة. يتم توفير خصائص مثل: تحسين محركات البحث، السرعة ..إلخ، بواسطة أدوات مثل Next.js وGatsby.

متى يكون Next.js أفضل من Gatsby؟

يمكن لكليهما المساعدة في العرض من جانب الخادوم SSR، ولكن بطريقتين مختلفتين.

النتيجة النهائية لـGatsby هي مولد موقع ساكن/ثابت، دون خادوم. تقوم بإنشاء الموقع، ثم نشر تطبيقك بشكل ثابت على Netlify أو موقع استضافة تشابهها.

يوفر Next.js واجهة خلفية Back-end يمكنها من جانب الخادوم عمل SSR، مما يسمح لك بإنشاء موقع ويب تفاعلي، مما يعني أنك ستنشره على نظام أساسي يمكنه تشغيل Node.js.

يمكن لـ Next.js إنشاء موقع ثابت أيضًا، لكنني لا أفضّل ذلك وأستخدم Gatsby بدلا عنها.

إذا كان هدفي هو إنشاء موقع ثابت، فسأواجه صعوبة في الاختيار وربما يمتلك Gatsby نظامًا بيئيًا أفضل من المكونات الإضافية الجاهزة، بما في ذلك العديد من المدوّنات على وجه الخصوص.

يعتمد Gatsby أيضًا بشكل كبير على GraphQL، وهو شيء قد تحبه أو تكرهه اعتمادًا على آرائك واحتياجاتك.

تنصيب Next.js

لتثبيت Next.js، يجب تثبيت Node.js.

تأكد من أن لديك أحدث إصدار من Node. تحقق من تنفيذ هذا الأمر `node -v على جهازك، وقارنه بأحدث إصدار LTS مدرج في https://nodejs.org/ .

بعد تثبيت Node.js، ستكون أوامر npmمتاحة في سطر الأوامر Terminal.

يمكننا اختيار مسارين الآن: استخدام create-next-app أو الأسلوب التقليدي الذي يتضمن تثبيت وإعداد تطبيق Next يدويًا.

باستخدام create-next-app

إذا كنت معتادًا على ذلك create-react-app،create-next-appفهذا هو الشيء نفسه -باستثناء أنه ينشئ تطبيق Next بدلاً من تطبيق React، كما يوحي الاسم.

أفترض أنك قمت بتثبيت Node.js سلفا، والذي يأتي مع الأمر npx بداية من الإصدار 5.2 مثلاً منذ أكثر من عامين في وقت كتابة هذا الكتيّب. تتيح لنا هذه الأداة المفيدة تنزيل أمر JavaScript وتنفيذه، وسنستخدمه على النحو التالي:

npx create-next-app

يطلب الأمر: اسم التطبيق وينشئ مجلدًا جديدًا لك بهذا الاسم نفسه، ثم يقوم بتنزيل جميع الحزم التي يحتاجها react، react-dom، next ، مجمّعة في ملف `package.json كما توضحه الصورة التالية:

20210407-18-15v95qx

يمكنك تشغيل المشروع عن طريق اﻷمر npm run dev:

image

وإليك النتيجة على http: //localhost:3000 :

20210407-18-17mz39x

هذه هي الطريقة المُوصى بها لبدء تطبيق Next.js، لأنها تمنحك هيكلًا ونموذجًا للتعليمات البرمجية الجاهزة. هناك أكثر من مجرد نموذج تطبيق افتراضي؛ يمكنك استخدام أي من الأمثلة المخزنة على https://github.com/zeit/next.js/tree/canary/examples باستخدام الخيار --example. على سبيل المثال جرب:

npx create-next-app --example blog-starter

مما يمنحك نسخة افتراضية لمدونة جاهزة قابلة للاستخدام على الفور مع معاينة بالألوان أيضا syntax highlighting:

20210407-18-upfd29

إنشاء تطبيق Next.js يدويًا

يمكنك تجنب طريقة create-next-app إذا كنت ترغب في إنشاء تطبيق Next من البداية. وإليك الفكرة:

قم بإنشاء مجلد فارغ في أي مكان تريد على جهازك، على سبيل المثال في المجلد الرئيس الخاص بك، انتقل إليه عبر كتابة اﻷوامر التالية:

mkdir nextjs
cd nextjs

وأنشئ أول مجلّد لمشروعك:

mkdir firstproject
cd firstproject

الآن استخدم npm لتهيئة مشروع Node:

npm init -y

يشير الخيار -yلـ npm لاستخدام الإعدادات الافتراضية لمشروع ما، وملء نموذج package.json للملف.

20210407-18-1g9hbcp

الآن قم بتثبيت Next وReact:

npm install next react react-dom

يجب أن يحتوي مجلد مشروعك الآن على ملفين:

الأول:

الثاني:

  • مجلد node_modules.

افتح مجلد المشروع باستخدام المحرر المفضل لديك. المحرر المفضل لدي هو Vscode. إذا كان برنامج vscode مثبتًا على جهازك، فيمكنك الأمر code .في الطرفية من فتح المجلد الحالي في المحرر إذا كان الأمر لا يعمل، فراجع هذا.

ملف package.json يحتوي الآن على هذا المحتوى:

{
  "name": "firstproject",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies":  {
    "next": "^10.1.1",
    "react": "^16.11.0",
    "react-dom": "^16.11.0"
  }
}

واستبدل جزء scripts بـ:

"scripts": {
  "dev": "next",
  "build": "next build",
  "start": "next start"
}

لإضافة أوامر بناء وتصدير مشروعنا على Next.js، التي سنستخدمها قريبًا.

نصيحة: استخدم "dev": "next -p 3001",لتغيير منفذ التشغيل، في هذا المثال، على المنفذ 3001.

20210407-18-1jn8xh4

الآن قم بإنشاء مجلد pagesوإضافة ملف index.js.

في هذا الملف، لننشئ مكوّن React الأول. سنقوم بتصديره افتراضيا:

const Index = () => (
  <div>
    <h1>Home page</h1>
  </div>
)

export default Index

الآن باستخدام الطرفية Terminal، قم بتشغيل npm run devلبدء خادوم التطوير Next.

سيتم تشغيل المشروع على منفذ 3000، على المضيف المحلي localhost.

20210407-18-nl5tv9

افتح http://localhost:3000 في متصفحك لرؤيته.

20210407-18-1siohrc

معاينة الشفرة المصدرية للتحقق من عمل SSR

دعنا نتحقق من أن التطبيق يعمل، يمكن ملاحظة قوة Next.js عند استخدامها، حيث يقوم بعرض الصفحات من جانب الخادوم ويتم تسليم الـHTML إلى المتصفّح، والذي له ثلاث فوائد رئيسة:

  • لا يحتاج متصفّح المستخدم إلى عرض React مما سيجعل التطبيق سريعاً.
  • ستقوم محركات البحث بفهرسة الصفحات دون الحاجة إلى تشغيل JavaScript من جانب المستخدم. بدأت محركات Google بحلّ هذه المشكلة، ولكن اعترفت لاحقاً بأنها عملية بطيئة وتأخر الأرشفة كما يجب عليك بصفتك منشئا للتطبيق مساعدة Google قدر الإمكان، إذا كنت ترغب في الحصول على ترتيب بحث جيد.
  • يمكنك الحصول على معاينات وصفية meta tags لمعلومات تطبيقك على مواقع التواصل الاجتماعي وتخصيص العنوان والوصف لأي من صفحاتك المشتركة على Facebook وTwitter وما إلى ذلك.

دعونا نلقي نظرة على مصدر التطبيق. باستخدام Chrome، يمكنك النقر بزر الفأرة الأيمن في أي مكان في الصفحة ، والضغط على "عرض مصدر الصفحة".

إذا قمت بعرض مصدر الصفحة ، فسترى <div><h1>Home page</h1></div>المقتطف في وسم HTML body، جنبًا إلى جنب مع مجموعة من ملفات JavaScript -حزم التطبيقات-.

لا نحتاج إلى إعدادات أخرى، فالعرض من جانب الخادوم SSR يعمل بالفعل.

سيتم تشغيل تطبيق React على متصفّح المستخدم، وسيكون هو أحد التفاعلات مثل النقر فوق الروابط. لكن إعادة تحميل الصفحة ستؤدي إلى إعادة تحميلها من جانب الخادوم. وباستخدام Next.js، يجب ألا يكون هناك اختلاف في النتيجة داخل المتصفح - يجب أن تبدو الصفحة التي يعرضها الخادوم تمامًا مثل الصفحة التي يعرضها المستخدم.

حُزم التطبيق (The app bundles)

عندما استعرضنا مصدر الصفحة، رأينا مجموعة من ملفات الجافاسكربت تم تضمينها:

سنضع الشفرة المصدرية في مُنسق HTML لإظهاره بشكل أفضل، حتى نتمكن من فهمه:

<!DOCTYPE html>
<html>

<head>
    <meta charSet="utf-8" />
    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1" />
    <meta name="next-head-count" content="2" />
    <link rel="preload" href="/_next/static/development/pages/index.js?ts=1572863116051" as="script" />
    <link rel="preload" href="/_next/static/development/pages/_app.js?ts=1572863116051" as="script" />
    <link rel="preload" href="/_next/static/runtime/webpack.js?ts=1572863116051" as="script" />
    <link rel="preload" href="/_next/static/runtime/main.js?ts=1572863116051" as="script" />
</head>

<body>
    <div id="__next">
        <div>
            <h1>Home page</h1></div>
    </div>
    <script src="/_next/static/development/dll/dll_01ec57fc9b90d43b98a8.js?ts=1572863116051"></script>
    <script id="__NEXT_DATA__" type="application/json">{"dataManager":"[]","props":{"pageProps":{}},"page":"/","query":{},"buildId":"development","nextExport":true,"autoExport":true}</script>
    <script async="" data-next-page="/" src="/_next/static/development/pages/index.js?ts=1572863116051"></script>
    <script async="" data-next-page="/_app" src="/_next/static/development/pages/_app.js?ts=1572863116051"></script>
    <script src="/_next/static/runtime/webpack.js?ts=1572863116051" async=""></script>
    <script src="/_next/static/runtime/main.js?ts=1572863116051" async=""></script>
</body>

</html>

لدينا 4 ملفات جافاسكريبت تم تحميلها مسبقا (preloaded) في head، باستخدام rel="preload" as="script":

  • /_next/static/development/pages/index.js 96 LOC
  • /_next/static/development/pages/_app.js 5900 LOC
  • /_next/static/runtime/webpack.js 939 LOC
  • /_next/static/runtime/main.js 12k LOC

هذا يعني أن المتصفّح سيقوم بتحميل الملفات (load) في أقرب وقت ممكن قبل أن يتم عمل تدفق للبيانات. دون ذلك، سيتم تحميل ملفات الجافاسكربت مع تأخير إضافي، وهذا يحسّن أداء تحميل الصفحة.

ثم يتم تحميل هذه الملفات الأربعة في نهاية وسم body، جنبًا إلى جنب مع:

  • /_next/static/development/dll/dll_01ec57fc9b90d43b98a8.js

ومقتطف JSON الذي يعيّن بعض الإعدادات الافتراضية لبيانات الصفحة:

<script id="__NEXT_DATA__" type="application/json">
{
  "dataManager": "[]",
  "props": {
    "pageProps":  {}
  },
  "page": "/",
  "query": {},
  "buildId": "development",
  "nextExport": true,
  "autoExport": true
}
</script>

تقوم ملفات الحزم الأربعة التي تم تحميلها بتنفيذ ميزة واحدة تسمى تقسيم الشفرة البرمجية. و index.jsيوفر ملف التعليمات البرمجية اللازمة لصفحة index الرئيسة، وإذا كانت لدينا المزيد من الصفحات سيكون لدينا المزيد من الحزم لكل صفحة، والتي ستقوم بتحميلها فقط إذا لزم الأمر واستعرض المستخدم تلك الصفحة -لتوفير وقت تحميل أسرع-

إلام يعني الرمز الذي يظهر أسفل يمين الصفحة؟

هل رأيت هذا الرمز الصغير في أسفل يمين الصفحة ، والذي يشبه البرق؟

إذا قمت بتمرير الفأرة فوقها، فستظهر صفحة "معروضة مسبقًا (Prerendered Page)":

يخبرك هذا الرمز، الذي _يظهر فقط في وضع التطوير (development mode)، أن الصفحة مؤهلة للتحسين التلقائي الثابت، مما يعني بشكل أساسي أنها لا تعتمد على البيانات التي يتم جلبها في وقت الاستدعاء، ويمكن عرضها مسبقًا وبناء ملف HTML ثابت في وقت البناء عند تشغيل npm run build.

ويمكن تحديد هذا من خلال عدم وجود getInitialProps() المرفقة داخل مكوّن الصفحة.

في هذه الحالة، يمكن أن تكون صفحتنا أسرع لأنها ستُقدم ملف HTML ثابت بدلاً من المرور عبر خادوم Node.js الذي يولّد ملفات HTML.

وأحيانا قد يظهر مثلث صغير كأيقونة، أو الصفحات غير معروضة مسبقًا (non-prerendered pages) وتعني مؤشّر تجميع ويظهر عند حفظ الصفحة. يقوم Next.js عند الحفظ بالتحميل الفوري للتغييرات لإعادة تحميل الشفرة المضافة حديثاً في التطبيق تلقائيًا.

إنها طريقة رائعة حقًا لتحديد ما إذا كان التطبيق قد تم تجميعه بالفعل ويمكنك اختبار الجزء الذي تعمل عليه.

تنصيب إضافة React DevTools

يعتمد Next.js على React، لذا فإن إحدى الأدوات المفيدة للغاية التي نحتاج إلى تثبيتها إذا لم تكن قد قمت بذلك هي أدوات React Developer.

تعد أدوات React Developer، المتوفرة لكل من Chrome و Firefox، أداة أساسية يمكنك استخدامها لفحص تطبيق React.

الآن، أدوات تطوير React ليست خاصة بـ Next.js فحسب، لكنني أريد توضيحها لأنك قد لا تكون على دراية كاملة بجميع الأدوات التي توفرها React.

إنها توفر المعاين (Inspector) يكشف عن شجرة مكونات React التي تبني صفحتك، ولكل مكوّن يمكنك الانتقال والتحقق من الخاصيات Props والحالة State والخطافات Hooks وغير ذلك الكثير.

وبمجرد الانتهاء من تثبيت React DevTools، يمكنك فتح الإضافة من متصفحك بمتصفّح Chrome، بزر الفأرة الأيمن على الصفحة، انقر فوق Inspect وستجد نافذتين جديدتين: المكونات (Components) و صفحة المحلّل (Profiler) .

إذا قمت بالنقر على نافذة المكونات، فسترى أنه في سيحدد المستعرض الأجزاء التي يتم تقديمها بواسطة هذا المكون.

ثم إذا حددت أي مكوّن في الشجرة، فستظهر لك لوحة اليمين مرجعًا للمكوِّن الأصلي، والخصائص الممرّرة إليه:

يمكنك التنقل بسهولة من خلال الضغط على أسماء المكوّنات.

وكذلك يمكنك النقر فوق رمز العين في شريط أدوات Developer Tools لفحص عنصر DOM. كما يمكنك استخدام رمز bug لتسجيل بيانات المكوّن في وحدة التحكم.

هذا رائع جدًا لأنه بمجرد طباعة البيانات هناك، يمكنك النقر بزر الفأرة الأيمن فوق أي عنصر والضغط على "تخزين كمتغير عام". على سبيل المثال، قمت باستخدام خاصية url، وتمكّنت من فحصها في وحدة التحكم باستخدام المتغير المؤقت المخصص لها temp1:

باستخدام خرائط المصدر (Source Maps)، التي يتم تحميلها بواسطة Next.js تلقائيًا في وضع التطوير، من لوحة المكونات، يمكننا النقر فوق الرمز <> وسيتحول DevTools إلى لوحة المصدر (Source panel)، مع عرض الشفرة المصدرية للمكوّن:

تعد نافذة المحلّل أكثر روعة، يسمح لنا بتسجيل التفاعلات في التطبيق ومعرفة ما سيحدث. لا يمكنني عرض مثال الآن، لأنه يحتاج إلى مكونين على الأقل لإنشاء تفاعل، ولدينا عنصر واحد فقط الآن. سأتحدث عن هذا لاحقًا.

img

لقد عرضت جميع لقطات الشاشة لمتصفّح Chrome، لكن أدوات React Developer تعمل بنفس الطريقة في Firefox:

img

تقنيات مختلفة يمكنك استعمالها لتنقيح الأخطاء البرمجية (Debugging Techniques)

بالإضافة إلى أدوات المطورين لـ React، والتي تعتبر ضرورية لبناء تطبيق Next.js، أودّ التأكيد على طريقتين لتصحيح أخطاء تطبيقات Next.js.

الأولى هي console.log()وجميع أدوات Console API. الطريقة التي تعمل بها تطبيقات Next ستجعل بيان السجل يعمل في وحدة تحكم المتصفح أو في الطرفية حيث بدأت باستخدام npm run dev.

على وجه الخصوص، إذا تم تحميل الصفحة من الخادوم، عندما توجه عنوان URL إليها، أو تضغط على زر التحديث / CMD / ctrl-R ، فإن أي تسجيل لوحدة التحكم يحدث في الجهاز.

أداة أخرى ضرورية هي دالة debugger. ستؤدي إضافة هذه العبارة إلى أحد المكونات إلى إيقاف عرض المتصفح للصفحة مؤقتًا:

img

رائعة حقًا لأنه يمكنك الآن استخدام مصحح أخطاء المتصفح لفحص القِيم وتشغيل تطبيقك سطرًا بسطر واحد في كل مرة.

يمكنك أيضًا استخدام المصحح في برنامج Vscode لتصحيح أخطاء التعليمات البرمجية من جانب الخادوم. هذه مقاطع تعليمية تشرح الأمر.

إضافة صفحات ثانية للمشروع

الآن بعد أن أصبح لدينا فهم جيد للأدوات التي يمكننا استخدامها لمساعدتنا في تطوير تطبيقات Next.js، دعنا نواصل من حيث توقفنا بتطبيقنا الأول:

img

عندما أريد إضافة صفحة ثانية لتطبيقنا، مثلا صفحة عرض التدوينات /blog، وفي الوقت الحالي سننشئ صفحة ثابتة بسيطة داخل مجلد pages، تمامًا مثل المكون الأول لدينا index.js:

20210415-18-17461ob

بعد حفظ الملف الجديد، ننفّذ الأمر npm run dev ، إذا قمت بتشغيله سابقاً فلا حاجة لإعادة تشغيله.

20210415-19-tc5irm

لاحظ ما أخبرتنا به الطرفية (Terminal):

img

رابط الصفحة /blog يعتمد على اسم الملف، و تموضعه داخل مجلد pages.

يمكنك إنشاء صفحة pages/hey/ho، وستظهر تلك الصفحة على شكل عنوان http://localhost:3000/hey/ho .

حاول تصفح الصفحة الجديد وعرض مصدرها، عند تحميلها من الخادوم، سيتم إدراج الحزمة /_next/static/development/pages/blog.js كواحدة من الحزم المحملة، ولن تجد/_next/static/development/pages/index.jsكما هو الحال في الصفحة الرئيسة. هذا لأنه بفضل التقسيم التلقائي للشفرة البرمجية، لا نحتاج إلى الحزمة التي تخدم الصفحة الرئيسة. فقط الحزمة التي تخدم صفحة المدونة.

img

يمكننا أيضًا تصدير وظيفة مجهولة الاسم من blog.js:

export default () => (
  <div>
    <h1>Blog</h1>
  </div>
)

أو إذا كنت تفضل صيغة الوظيفة العادية:

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

ربط صفحتين مع بعض

إحدى مميزات Next.js تمكنك من ربط الصفحات مع بعضها البعض بحيث تكون أسهل وسريعة للغاية.

لو نستعمل الطريقة القديمة بالاسناد عبر a tags وتفحّصنا إضافة DevTool وفعّلنا زر PreserLog نجد أنه قام بتحميل جميع الحزم ولكن نحن لسنا بحاجة لجميع هذه، سنحتاج فقط إلى حزمة blog.js، لإصلاح هذه المشكلة سنستعمل مكتبة توفّرها Next تدعى Link.

نقوم باستيرادها:

import Link from 'next/link'

ثم نستخدمها بتضمين a tags، مثل هذه:

import Link from 'next/link'

const Index = () => (
  <div>
    <h1>Home page</h1>
    <Link href='/blog'>
      <a>Blog</a>
    </Link>
  </div>
)

export default Index

الآن إذا أعدنا محاولة فحص الصفحة مثلما فعلنا سابقًا، فستتمكن من رؤية أن صفحة blog.js يتم تحميل الحزمة الخاصة بها فقط:

img

تم تحميل الصفحة بشكل أسرع، حتى أن أيقونة التحميل بأعلى صفحة التبويب لن تظهر.

ماذا لو ضغطت الآن على زر العودة؟ لن يتم تحميل أي شيء، لأن المتصفح لا يزال يحتوي على الحزمة القديمة index.js، وجاهزة لتحميل صفحة/index. كل شيء تلقائي!

المحتوى التفاعلي (Dynamic content) مع المُوجّه (Router )

رأينا في الفصل السابق كيفية ربط الصفحة الرئيسة بصفحة المدونة. والآن لو أردنا عرض روابط التدوينات بشكل ديناميكي. على سبيل المثال، قد تكون تدوينة بعنوان "Hello World" ورابطها /blog/hello-world. وقد تكون تدوينة ثانية بعنوان "مشاركتي الثانية" /blog/my-second-post.

يمكن لـ Next.js تقديم محتوى ديناميكي بناءً على الروابط الديناميكية (dynamic URL) .

نقوم بإنشاء رابط ديناميكي عن طريق إنشاء صفحة ديناميكية مع [].

كيف؟ سنضيف ملف pages/blog/[id].js. وهذا الملف يتعامل مع كل الروابط الديناميكية تحت الرابط /blog/، مثل تلك التي أشرنا إليها أعلاه: /blog/hello-world إلى آخره.

في اسم الملف، نضع المعرّف [id] داخل الأقواس المربعة وتعني أن أي شيء ديناميكي متغيّر سيتم وضعه داخل المعرّف id معلمة لخاصية الاستعلام (query property) الخاصة بالمُوجّه (Router) .

حسنًا، قد تحدثت عن أشياء كثيرة في وقت واحد.

ماذا أقصد بالمُوجّه (Router)؟

الموجه عبارة عن مكتبة مقدمة من Next.js. نستوردها من next/router:

import { useRouter } from 'next/router'

وبمجرد الانتهاء من ذلك نعرّف useRouter، بإنشاء مثيل لكائن الموجّه باستخدام:

const router = useRouter()

بمجرد أن نعرّف كائن الموجّه هذا، يمكننا استخراج المعلومات منه.

على وجه المثال، يمكننا الحصول على الجزء الديناميكي من عنوان URL في ملف [id].js عن طريق الوصول إليه عبر router.query.id.

يمكن أن يكون الجزء الديناميكي أيضًا مجرد جزء من عنوان URL، مثل post-[id].js.

لذلك دعونا نستمر ونطبق كل هذه الأشياء عمليًا.

قم بإنشاء الملف على مسار pages/blog/[id].js

import { useRouter } from 'next/router'

export default () => {
  const router = useRouter()

  return (
    <>
      <h1>Blog post</h1>
      <p>Post id: {router.query.id}</p>
    </>
  )
}

الآن إذا تصفّحت الرابط http://localhost:3000/blog/test ، سترى هذه:

img

يمكننا استخدام المعرّف id لجلب تدوينة معيّنة من قائمة التدوينات بقاعدة البيانات، لتبسيط الأمور على سبيل المثال، سنضيف ملفposts.json في المجلد الرئيسي للمشروع:

{
  "test": {
    "title": "test post",
    "content": "Hey some post content"
  },
  "second": {
    "title": "second post",
    "content": "Hey this is the second post content"
  }
}

الآن يمكننا استيراده والبحث عن التدوينة من المعرّف id:

import { useRouter } from 'next/router'
import posts from '../../posts.json'

export default () => {
  const router = useRouter()

  const post = posts[router.query.id]

  return (
    <>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </>
  )
}

عند إعادة تحميل الصفحة المفروض ستظهر لنا هذه:

img

لكنها ليست كذلك! بل حصلنا على خطأ في وحدة التحكم وخطأ في المتصفح أيضًا:

img

لماذا؟ لأنه أثناء العرض، وعندما تتم تهيئة المكون، لم تجهّز البيانات بعد. سنرى كيفية توفير البيانات للمكون باستخدام getInitialProps في الدرس التالي.

في الوقت الحالي، أضف القليل من التحقق if (!post) return <p></p> قبل إرجاع JSX:

import { useRouter } from 'next/router'
import posts from '../../posts.json'

export default () => {
  const router = useRouter()

  const post = posts[router.query.id]
  if (!post) return <p></p>

  return (
    <>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </>
  )
}

الآن يجب أن تعمل الصفحة بشكل عادي. في البداية يتم تقديم المكون بدون إستعلام عن البيانات router.query.id. وبعد التقديم، يقوم Next.js بتشغيل تحديث بقيمة الاستعلام وتعرض الصفحة المعلومات الصحيحة.

وإذا عرضت المصدر، ستجد هذه العلامة الفارغة <p> في HTML:

img

سنقوم قريبًا بإصلاح هذه المشكلة التي تفشل في تنفيذ SSR وهذا يؤخر أوقات التحميل لمستخدمينا وتحسين محركات البحث كما تحدثنا سابقاً.

يمكننا إكمال مثال المدونة بإدراج تلك التدوينات في pages/blog.js:

import posts from '../posts.json'

const Blog = () => (
  <div>
    <h1>Blog</h1>

    <ul>
      {Object.entries(posts).map((value, index) => {
        return <li key={index}>{value[1].title}</li>
      })}
    </ul>
  </div>
)

export default Blog

ويمكننا ربطهم بصفحات التدوينات فرادى، عن طريق إستيراد مكتبة Link منها next/link واستخدامها داخل حلقة التدوينات:

import Link from 'next/link'
import posts from '../posts.json'

const Blog = () => (
  <div>
    <h1>Blog</h1>

    <ul>
      {Object.entries(posts).map((value, index) => {
        return (
          <li key={index}>
            <Link href='/blog/[id]' as={'/blog/' + value[0]}>
              <a>{value[1].title}</a>
            </Link>
          </li>
        )
      })}
    </ul>
  </div>
)

export default Blog


وصلنا إلى ختام هذا الجزء من السلسلة، سأكملها كل يومين أو ثلاثة بحول الله.

لا تنسى الاشتراك في القائمة البريدية، لأرسل لك تحديثات السلسلة باذن الله تعالى. ومتابعتي على تويتر و Linkedin.

دمتم بود أحبتي. دعواتكم لنا بالتوفيق والصحة والسلامة والعافية.




اشترك بالقائمة البريدية لمدونتي

شارك بالقائمة البريدية لتحصل على جديد التدوينات كل يوم سبت