بناء تطبيق ويب ديناميكي لويكي Rick and Morty باستخدام Next.js: دليل شامل

دقائق القراءة: 16

بناء تطبيق ويب ديناميكي لويكي Rick and Morty باستخدام Next.js: دليل شامل

يُعد بناء تطبيقات الويب التي تعتمد على واجهات برمجة التطبيقات (APIs) الديناميكية والعرض من جانب الخادم (SSR) طريقة ممتازة لتقديم تجربة مستخدم فريدة، تجمع بين المحتوى الغني والسرعة الفائقة. في هذا المقال، سنتعمق في كيفية استغلال إمكانيات Next.js لبناء مثل هذه التطبيقات بكفاءة ويسر.

ماذا سنبني في هذا الدليل؟

سنخوض تجربة ممتعة لبناء تطبيق ويب بسيط يعمل كويكي (Wiki) لشخصيات مسلسل Rick and Morty الشهير. سيتضمن تطبيقنا الميزات الأساسية التالية:

  • قائمة بالشخصيات معروضة على الصفحة الرئيسية.
  • زر لتحميل المزيد من الشخصيات، حيث أن واجهة برمجة التطبيقات (API) تدعم التصفح (Pagination).
  • صندوق بحث للبحث عن شخصيات محددة.
  • صفحة تفصيلية لكل شخصية تعرض معلوماتها الأساسية.

معاينة لتطبيق Rick and Morty Wiki مبني باستخدام Next.js

من خلال هذا المشروع، سنتعلم مفاهيم أساسية مثل:

  • كيفية إعداد تطبيق ويب باستخدام Next.js.
  • كيفية جلب البيانات واستخدامها من واجهة برمجة تطبيقات (API).
  • كيفية العرض المسبق للبيانات (Pre-rendering) من واجهة برمجة تطبيقات.
  • كيفية إعداد التوجيه الديناميكي (Dynamic Routing).

ما هو Next.js؟

Next.js هو إطار عمل (Framework) مبني على React، تم تطويره بواسطة شركة Vercel. يتيح لك بناء تطبيقات ويب ديناميكية وخفيفة الوزن بسهولة، مع مجموعة واسعة من الميزات الحديثة المدمجة جاهزة للاستخدام. أما Vercel، الشركة الداعمة لـ Next.js، فهي خدمة تتيح لك أتمتة مسارات التطوير المستمر (Continuous Development Pipelines) لنشر تطبيقات الويب بسهولة حول العالم. سنتعلم أيضًا كيفية استخدام أداة سطر الأوامر الخاصة بـ Vercel لنشر تطبيق الويكي الجديد الخاص بنا بشكل اختياري.

الخطوة 0: إعداد تطبيق Next.js جديد

للبدء، دعنا ننشئ مشروع Next.js الخاص بنا. سنستخدم npm أو yarn للبدء:

yarn create next-app
# أو
npx create-next-app

إنشاء تطبيق Next.js جديد باستخدام سطر الأوامر

بمجرد تشغيل هذا الأمر، سيطرح عليك بعض الأسئلة. سأسمي مشروعي my-rick-and-morty-wiki، ولكن يمكنك تسميته بما ترغب فيه. سيطلب منك بعد ذلك اختيار القالب (Template) — اختر القالب الافتراضي. أخيرًا، سيقوم بتثبيت جميع التبعيات (Dependencies).

تم إنشاء تطبيق Next.js بنجاح في نافذة الطرفية

عند الانتهاء، يمكنك الانتقال إلى الدليل الجديد وتشغيل الأمر التالي:

yarn dev
# أو
npm run dev

بدء تشغيل خادم تطوير Next.js محليًا

يجب أن يكون لديك الآن خادم محلي يعمل على العنوان http://localhost:3000!

القالب الافتراضي لتطبيق Next.js الجديد

الخطوة 1: جلب شخصيات Rick and Morty باستخدام API في Next.js

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

حاليًا، لا تطلب هذه الصفحة أي بيانات. للحصول على شخصياتنا، سننتقل مباشرة إلى طلب هذه البيانات من جانب الخادم (Server-Side). للقيام بذلك، يتيح لنا Next.js تصدير دالة غير متزامنة (async) تسمى getServerSideProps مباشرة بجانب صفحتنا، والتي سيستخدمها لحقن صفحتنا بأي بيانات نقوم بجلبها. دعنا نبدأ بإضافة المقتطف التالي فوق مكون دالة Home:

const defaultEndpoint = `https://rickandmortyapi.com/api/character/`;

export async function getServerSideProps() {
  const res = await fetch(defaultEndpoint)
  const data = await res.json();

  return {
    props: {
      data
    }
  }
}

إليك ما نقوم به هنا:

  • نقوم بتعيين متغير يسمى defaultEndpoint يحدد ببساطة نقطة نهاية (API endpoint) الافتراضية لواجهة برمجة التطبيقات.
  • نقوم بتعريف دالة getServerSideProps التي سنستخدمها لجلب بياناتنا.
  • داخل هذه الدالة، نستخدم أولاً واجهة fetch API لإنشاء طلب إلى نقطة النهاية الخاصة بنا.
  • باستخدام استجابتها، نقوم بتشغيل طريقة json حتى نتمكن من الحصول على الإخراج بتنسيق JSON.
  • أخيرًا، نقوم بإرجاع كائن حيث نجعل بياناتنا (data) متاحة كخاصية (prop) في خاصية props.

الآن بعد أن قمنا بإنشاء هذا الطلب، نحتاج إلى جعله متاحًا للاستخدام. يتم توفير بياناتنا (data) كخاصية، لذا دعنا ننشئ وسيطة في دالة مكون Home الخاصة بنا للحصول عليها:

export default function Home({ data }) {

لاختبار ذلك، يمكننا استخدام console.log لرؤية النتائج:

export default function Home({ data }) {
  console.log('data', data);
}

وبمجرد الحفظ وإعادة تحميل الصفحة، يمكننا الآن رؤية نتائجنا!

تسجيل بيانات شخصيات Rick and Morty التي تم عرضها من جانب الخادم في تطبيق Next.js

الخطوة 2: عرض شخصيات Rick and Morty على الصفحة

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

  • عنوان <h1> ليصبح "Wubba Lubba Dub Dub!".
  • وصف <p> ليصبح "Rick and Morty Character Wiki".

سأقوم أيضًا بتحديث محتويات <div className="grid"> إلى:

<ul className="grid">
  <li className="card">
    <a href="https://nextjs.org/docs">
      <h3>My Character</h3>
    </a>
  </li>
</ul>

ما أفعله هنا:

  • أجعل عنصر <div> قائمة (<ul>) ليكون أفضل لسهولة الوصول (Accessibility).
  • أجعل عنصر <li> داخل <ul> هو card.
  • أقوم بتغيير <h3> مؤقتًا إلى "My Character".

للتأكد من أن <ul> الجديد لا يفسد التخطيط بأنماطه الافتراضية، دعنا نضيف أيضًا ما يلي إلى أسفل قواعد CSS الخاصة بـ .grid:

list-style: none;
margin-left: 0;
padding-left: 0;

والآن إذا نظرنا إلى الصفحة، يجب أن نرى تغييراتنا الأساسية.

عنوان محدث في ويكي Rick and Morty الأساسي

بعد ذلك، دعنا نجعل شبكتنا (Grid) تحمل شخصياتنا. في الجزء العلوي من دالة مكون Home، دعنا نضيف:

const { results = [] } = data;

سيقوم هذا بفك هيكلة (Destructure) مصفوفة results من كائن البيانات (data). بعد ذلك، دعنا نحدث كود الشبكة الخاص بنا:

<ul className="grid">
  {results.map(result => {
    const { id, name, image } = result;
    return (
      <li key={id} className="card">
        <a href="#">
          <img src={image} alt={`${name} Thumbnail`} />
          <h3>{name}</h3>
        </a>
      </li>
    )
  })}
</ul>

إليك ما نقوم به:

  • نستخدم طريقة map لإنشاء عنصر قائمة جديد لكل نتيجة من نتائجنا (أو شخصياتنا).
  • داخل ذلك، نحصل على id و name و image من كل نتيجة شخصية.
  • نستخدم الـ ID كمفتاح (key) لعنصر القائمة الخاص بنا لإرضاء React.
  • نقوم بتحديث رأس الصفحة (<h3>) باسم الشخصية (name).
  • نقوم بإضافة صورة الشخصية باستخدام <img src={image} alt={`${name} Thumbnail`} />.

وبمجرد الحفظ وإعادة تحميل الصفحة، يجب أن نرى الآن قائمة جديدة بشخصياتنا من واجهة برمجة التطبيقات!

قائمة ديناميكية بأسماء وصور شخصيات Rick and Morty

الخطوة 3: تحميل المزيد من شخصيات Rick and Morty

إذا لاحظت، عند تحميل الصفحة، نحصل على عدد معين فقط من النتائج. بشكل افتراضي، لن تُرجع واجهة برمجة التطبيقات القائمة الكاملة للشخصيات، وهو أمر منطقي لأنها طويلة جدًا! بدلاً من ذلك، تستخدم التصفح (Pagination) وتوفر لنا نقطة النهاية "التالية" (next endpoint)، أو الصفحة التالية من النتائج، مما سيسمح لنا بتحميل المزيد من النتائج.

للبدء، سنستخدم خطاف useState من React لتخزين نتائجنا في الحالة (State). سيكون لدينا بعد ذلك القدرة على تحديث تلك الحالة بالمزيد من النتائج. أولاً، دعنا نستورد useState من React:

import { useState } from 'react';

بعد ذلك، دعنا ننشئ حالتنا عن طريق إعادة تسمية متغير results الأصلي أولاً وإعداد مثيل useState الخاص بنا:

const { results: defaultResults = [] } = data;
const [results, updateResults] = useState(defaultResults);

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

أولاً، نريد تحديث عبارة فك الهيكلة (Destructuring) الخاصة بنا مع data للحصول على قيمة info:

const { info, results: defaultResults = [] } = data;

بعد ذلك، دعنا نعد بعض الحالات باستخدام ذلك:

const [page, updatePage] = useState({
  ...info,
  current: defaultEndpoint
});

هنا، نقوم بما يلي:

  • إنشاء حالة page جديدة يمكننا استخدامها للحصول على قيم prev و next.
  • نقوم أيضًا بإنشاء قيمة جديدة تسمى current سنبدأ باستخدام defaultEndpoint الخاص بنا، والذي كان الطلب الذي تم إجراؤه على الخادم.

الفكرة هنا هي، عندما نريد تحميل المزيد من النتائج، سنقوم بإعداد كود لمراقبة قيمة current وتحديث تلك القيمة بـ next، بحيث عندما تتغير، سنقوم بإنشاء طلب جديد. للقيام بذلك، دعنا نضيف خطاف useEffect لإنشاء هذا الطلب:

const { current } = page;

useEffect(() => {
  if ( current === defaultEndpoint ) return;

  async function request() {
    const res = await fetch(current)
    const nextData = await res.json();

    updatePage({
      current,
      ...nextData.info
    });

    if ( !nextData.info?.prev ) {
      updateResults(nextData.results);
      return;
    }

    updateResults(prev => {
      return [
        ...prev,
        ...nextData.results
      ]
    });
  }
  request();
}, [current]);

إليك ما يحدث:

  • أولاً، نقوم بفك هيكلة قيمة current من page.
  • نقوم بإنشاء خطاف useEffect يستخدم current كاعتمادية (dependency). إذا تغيرت القيمة، سيتم تشغيل الخطاف.
  • إذا كانت قيمة current هي نفسها defaultEndpoint، فإننا لا نشغل الكود، حيث أن لدينا بالفعل بيانات طلبنا. هذا يمنع طلبًا إضافيًا عند التحميل.
  • نقوم بإنشاء دالة غير متزامنة (async) يمكننا تشغيلها. يتيح لنا هذا استخدام async/await داخل خطاف useEffect الخاص بنا.
  • نقوم بإنشاء طلب إلى نقطة النهاية current. مع هذا الطلب الناجح، نقوم بتحديث حالة page بمعلومات info الجديدة مثل قيمتي prev و next الجديدتين.
  • إذا لم يكن لطلبنا قيمة سابقة (prev)، فهذا يعني أنها المجموعة الأولى من النتائج للطلب المحدد، لذا يجب أن نستبدل نتائجنا بالكامل للبدء من جديد.
  • إذا كان لدينا قيمة سابقة، نقوم بدمج (concatenate) نتائجنا الجديدة مع القديمة، حيث أن ذلك يعني أننا طلبنا للتو الصفحة التالية من النتائج.

مرة أخرى، إذا قمت بحفظ الصفحة وإعادة تحميلها، فلن يؤدي هذا إلى أي شيء ويجب أن تكون صفحتك كما كانت من قبل. أخيرًا، سنقوم بإنشاء زر "Load More" واستخدامه لتحديث قيمة current لإطلاق طلب جديد عندما نريد صفحة جديدة. للقيام بذلك، دعنا أولاً نضيف زرًا جديدًا أسفل شبكتنا:

<p>
  <button>Load More</button>
</p>

زر 'تحميل المزيد' مضاف إلى قائمة شخصيات Rick and Morty Wiki

الآن نريد أن يحدث شيء عندما ننقر على هذا الزر، لذا أضف أولاً معالج حدث (Event Handler):

<button onClick={handleLoadMore}>Load More</button>

ثم فوق عبارة الإرجاع (return statement) للمكون، دعنا نضيف هذه الدالة:

function handleLoadMore() {
  updatePage(prev => {
    return {
      ...prev,
      current: page?.next
    }
  });
}

عند تشغيلها بنقرة الزر، ستقوم هذه الدالة بتحديث حالة page الخاصة بنا بقيمة current جديدة، وتحديدًا بقيمة next التي هي نقطة النهاية لجلب صفحتنا التالية من النتائج.

تحميل المزيد من الشخصيات في Rick and Morty Wiki

وعندما نحفظ ونعيد تحميل الصفحة، فإنها تفعل ذلك تمامًا!

الخطوة 4: إضافة القدرة على البحث عن شخصيات Rick and Morty

إحدى الميزات التي توفرها واجهة برمجة تطبيقات Rick and Morty API هي القدرة على تصفية النتائج — أي القدرة على البحث. لذا دعنا نضيف ذلك كميزة.

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

<form className="search">
  <input name="query" type="search"/>
  <button>Search</button>
</form>

بعد ذلك، دعنا نضيف هذه الأنماط إلى أسفل كتلة <style jsx> الأولى:

.search input {
  margin-right: .5em;
}

@media (max-width: 600px) {
  .search input {
    margin-right: 0;
    margin-bottom: .5em;
  }

  .search input,
  .search button {
    width: 100%;
  }
}

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

نموذج بحث مضاف إلى Rick and Morty Wiki

لا يفعل شيئًا بعد، لذا دعنا نجعله يبحث عند إرسال النموذج. للبدء، دعنا نضيف سمة onSubmit إلى نموذجنا:

<form className="search" onSubmit={handleOnSubmitSearch}>

ولمواكبة ذلك، دعنا نحدد دالة الإرسال (Submit Function) الخاصة بنا فوق عبارة الإرجاع (return statement):

function handleOnSubmitSearch(e) {
  e.preventDefault();

  const { currentTarget = {} } = e;
  const fields = Array.from(currentTarget?.elements);
  const fieldQuery = fields.find(field => field.name === 'query');

  const value = fieldQuery.value || '';
  const endpoint = `https://rickandmortyapi.com/api/character/?name=${value}`;

  updatePage({
    current: endpoint
  });
}

إليك ما نقوم به:

  • أولاً، نمنع السلوك الافتراضي لإرسال النموذج لمنع الصفحة من إعادة التحميل باستخدام e.preventDefault().
  • بعد ذلك، نلتقط الهدف الحالي (currentTarget)، وهو نموذجنا.
  • نلتقط الحقول من النموذج باستخدام خاصية elements. نحول هذا أيضًا إلى مصفوفة (Array) لسهولة التعامل معها.
  • نبحث في تلك الحقول عن مدخل الاستعلام (query input) الخاص بنا.
  • نلتقط قيمة هذا المدخل.
  • ننشئ نقطة نهاية جديدة حيث نقوم بالتصفية حسب الاسم (name) باستخدام قيمة الاستعلام هذه.
  • أخيرًا، نقوم بتحديث خاصية current في حالة صفحتنا لتشغيل طلب جديد إلى نقطة النهاية تلك.

وبمجرد حفظ ذلك وإعادة تحميل الصفحة، يمكنك الآن تجربة البحث. يجب أن تكون قادرًا على كتابة اسم مثل "rick"، والضغط على Enter أو النقر على زر البحث، ويجب أن ترى الآن نتائج مصفاة مع مختلف شخصيات Rick عبر الكون!

البحث عن شخصية 'Rick' في Rick and Morty Wiki

الخطوة 5: استخدام المسارات الديناميكية للربط بصفحات شخصيات Rick and Morty

الآن بعد أن أصبح لدينا جميع شخصياتنا، نريد أن نكون قادرين على النقر على تلك الشخصيات وعرض بعض التفاصيل الإضافية. للقيام بذلك، سنستفيد من المسارات الديناميكية (Dynamic Routes) في Next.js.

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

- pages
  -- character
    --- [id]
      -- index.js

نعم، هذا يعني أنك تقوم حرفيًا بإنشاء مجلد باسم [id]، وهذا ليس المقصود استبداله. يتعرف Next.js على هذا النمط وسيسمح لنا باستخدامه لإنشاء مسار ديناميكي.

لجعل إنشاء الصفحة أسهل، سنقوم ببساطة بمضاعفة صفحتنا الرئيسية عن طريق نسخ ملف pages/index.js الخاص بنا إلى الدليل الجديد. لذلك يجب أن يكون لدينا الآن صفحة جديدة في pages/character/[id]/index.js.

بعد ذلك، دعنا نزيل مجموعة من الأشياء حتى نتمكن من الوصول إلى نقطة بداية جيدة:

  • إزالة كل شيء فوق عبارة return في مكون دالة صفحتنا.
  • إعادة تسمية مكون الدالة إلى Character.
  • إزالة استيرادات useState و useEffect.
  • إزالة الوصف، نموذج البحث، الشبكة، وزر تحميل المزيد.
  • اختياري: إزالة التذييل (Footer).

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

export default function Character({ data }) {
  return (
    <div className="container">
      <Head>
        <title>Create Next App</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main>
        <h1 className="title">Wubba Lubba Dub Dub!</h1>
      </main>

بينما توجد بعض أنماط CSS التي لا نحتاجها، سنتركها جميعًا هنا لهذا العرض التوضيحي. لا تتردد في تنظيف بعضها لاحقًا. إذا انتقلت يدويًا إلى /character/1، يجب أن ترى الآن صفحة بسيطة تحتوي على عنوان فقط:

صفحة شخصية بسيطة مع عنوان أساسي

بعد ذلك، دعنا نحدث البيانات التي نجلبها. يمكننا إعادة استخدام معظم الكود في دالة getServerSideProps الخاصة بنا. سنضيف وسيطة جديدة إلى دالة getServerSideProps هذه:

export async function getServerSideProps({ query }) {

عند عرض صفحتنا، يقوم Next.js بحقن البيانات في صفحتنا ودالة getServerSideProps حول البيئة. هنا، نقوم بفك هيكلة تلك البيانات للحصول على كائن query الذي سيتضمن أي سمات توجيه ديناميكية، مثل [id] الذي نحدده في المسار.

بعد ذلك، في الجزء العلوي من دالة getServerSideProps، دعنا نفك هيكلة الـ ID:

const { id } = query;

وأخيرًا، دعنا نستخدم هذا الـ ID لإنشاء نقطة نهاية ديناميكيًا سنستخدمها لجلب بيانات شخصيتنا:

const res = await fetch(`${defaultEndpoint}${id}`);

هنا، نستخدم نقطة نهاية الشخصية الخاصة بنا ونلحق الـ ID الديناميكي لعنوان URL الخاص بنا بنهاية عنوان URL. لاختبار ذلك، دعنا نضيف console.log إلى الجزء العلوي من دالة Character:

export default function Character({ data }) {
  console.log('data', data);
  // ...

وإذا قمنا بالحفظ وإعادة تحميل صفحتنا، يجب أن نرى الآن تفاصيل المستخدم حول الشخصية رقم 1 مسجلة، وهي Rick Sanchez!

تسجيل بيانات شخصية Rick and Morty الفردية في وحدة التحكم

لدينا البيانات، دعنا نضيفها إلى صفحتنا. في الجزء العلوي من دالة الشخصية، دعنا نضيف عبارة فك الهيكلة هذه:

const { name, image, gender, location, origin, species, status } = data;

يمنحنا هذا مجموعة من السمات التي نحصل عليها مباشرة من كائن البيانات الذي رأيناه مسجلًا. لاستخدام ذلك، يمكننا البدء بتحديث العنوان إلى هذا الاسم:

<title>{name}</title>

وأيضًا <h1>:

<h1 className="title">{name}</h1>

في هذه المرحلة، يجب أن نرى الآن اسم Rick ديناميكيًا.

عنوان صفحة شخصية Rick and Morty ديناميكي

بعد ذلك، دعنا نضيف هذه الكتلة أسفل <h1> لتضمين المزيد من تفاصيل شخصيتنا:

<div className="profile">
  <div className="profile-image">
    <img src={image} alt={name} />
  </div>
  <div className="profile-details">
    <h2>Character Details</h2>
    <ul>
      <li><strong>Name:</strong> {name}</li>
      <li><strong>Status:</strong> {status}</li>
      <li><strong>Gender:</strong> {gender}</li>
      <li><strong>Species:</strong> {species}</li>
      <li><strong>Location:</strong> {location?.name}</li>
      <li><strong>Originally From:</strong> {origin?.name}</li>
    </ul>
  </div>
</div>

هنا نستخدم صورة شخصياتنا (image) لعرض صورة لشخصيتنا وبيانات وصفية أخرى متنوعة لإضافة تفاصيل الشخصية. يمكننا متابعة ذلك بإضافة هذا المقتطف من CSS إلى أنماطنا:

.profile {
  display: flex;
  margin-top: 2em;
}

@media (max-width: 600px) {
  .profile {
    flex-direction: column;
  }
}

.profile-image {
  margin-right: 2em;
}

@media (max-width: 600px) {
  .profile-image {
    max-width: 100%;
    margin: 0 auto;
  }
}

والآن لدينا سيرة ذاتية لشخصيتنا!

صفحة شخصية Rick Sanchez ديناميكية في Rick and Morty Wiki

ملخص سريع، لدينا صفحتنا الديناميكية الجديدة. يمكننا الانتقال إلى /character/1 أو أي ID آخر لرؤية شخصية محددة. دعنا الآن نحدث صفحتنا الرئيسية للربط بهذه الصفحات. بالعودة إلى pages/index.js، صفحتنا الرئيسية، دعنا أولاً نستورد مكون Link من Next.js:

import Link from 'next/link';

بعد ذلك، داخل شبكتنا حيث نقوم بالمرور عبر قائمة النتائج باستخدام map، دعنا نستخدم مكون <Link> ونحدث الكود الخاص بنا:

<li key={id} className="card">
  <Link href="/character/[id]" as={`/character/${id}`}>
    <a>
      <img src={image} alt={`${name} Thumbnail`} />
      <h3>{name}</h3>
    </a>
  </Link>
</li>

إليك ما نقوم به:

  • أولاً، نلف عنصر <a> بمكون <Link>.
  • نضيف خصائص href و as لوصف الصفحة التي نريد الربط بها إلى Next.js. نحتاج إلى استخدام خاصية as لأنها رابط ديناميكي.
  • نزيل خاصية href من عنصر <a> حيث يتم تطبيقها الآن على عنصر <Link>.

إذا قمنا بالحفظ وإعادة تحميل صفحتنا الرئيسية، سنلاحظ أنه لم يتغير شيء، ولكن عندما ننقر على أي من شخصياتنا، ننتقل الآن إلى صفحة سيرتهم الذاتية!

التنقل إلى صفحة شخصية Jerry Smith في Rick and Morty Wiki

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

import Link from 'next/link';

في الجزء السفلي من وسم <main> أسفل div الخاص بـ .profile، دعنا نضيف هذا الكود:

<p className="back">
  <Link href="/">
    <a>Back to All Characters</a>
  </Link>
</p>

ويمكننا إضافة الأنماط الأساسية التالية لجعله يبدو كـ رابط ببساطة:

.back a {
  color: blue;
  text-decoration: underline;
}

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

رابط 'العودة إلى جميع الشخصيات' في صفحة شخصية Rick and Morty

خطوة إضافية: نشر ويكي Rick and Morty الخاص بك على Vercel!

نظرًا لأننا نستخدم Next.js، فإن Vercel يجعل نشر تطبيقنا أمرًا بسيطًا للغاية. للقيام بذلك، نحتاج إلى تثبيت أداة سطر الأوامر Vercel CLI. يمكننا القيام بذلك عن طريق تثبيتها كوحدة npm عالميًا:

yarn global add vercel
# أو
npm i -g vercel

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

تسجيل الدخول إلى Vercel CLI عبر الطرفية

مع تثبيت Vercel CLI، يمكننا ببساطة تشغيل vercel في دليل مشروعنا، وملء بعض الأسئلة، وسيتم نشره تلقائيًا!

نشر التطبيق باستخدام Vercel CLI

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

تطبيق Rick and Morty Wiki النهائي المنشور على Vercel

ماذا يمكننا أن نفعل أيضًا؟

هذا المشروع هو نقطة انطلاق رائعة، وهناك العديد من التحسينات والميزات التي يمكنك إضافتها:

المزيد من الصفحات الديناميكية

في كل مرة تقوم فيها بتقديم طلب لشخصية، تُرجع واجهة برمجة التطبيقات نقاط نهاية أخرى يمكنك استخدامها مثل المواقع (locations) والحلقات (episodes). يمكننا الاستفادة من نقاط النهاية هذه وإنشاء صفحات ديناميكية جديدة، تمامًا مثل صفحات ملفات تعريف الشخصيات الديناميكية الخاصة بنا، للسماح للأشخاص برؤية المزيد من المعلومات حول موقع أو حلقة معينة.

إضافة بعض الأنماط الجذابة

لقد التزمنا ببعض الأنماط الأساسية التي تضمنها Next.js وأضفنا بعض الأنماط الأساسية فقط لأغراض العرض. ولكن الآن بعد أن انتهيت، يمكنك الاستمتاع وتخصيصها لتناسب ذوقك وإبداعك!

إضافة فلاتر للشخصيات

بالإضافة إلى التصفية حسب الاسم، تدعم واجهة برمجة التطبيقات أيضًا التصفية حسب الحالة (status). عن طريق إضافة معامل status إلى عنوان URL الخاص بنقطة النهاية، تمامًا مثل معامل name الخاص بنا، يمكنك إضافة فلتر جديد لتسهيل العثور على الشخصيات التي لا تزال على قيد الحياة أو غير ذلك.

الخلاصة التقنية

لقد أثبت هذا الدليل العملي مدى قوة ومرونة إطار عمل Next.js في بناء تطبيقات الويب الديناميكية والمعقدة. من خلال الاستفادة من ميزات مثل العرض من جانب الخادم (getServerSideProps) والمسارات الديناميكية (Dynamic Routes) وإدارة الحالة (useState و useEffect)، تمكنا من إنشاء تطبيق ويب تفاعلي بالكامل يجلب البيانات من واجهة برمجة تطبيقات خارجية ويعرضها بكفاءة. كما أن سهولة النشر على Vercel تؤكد على فلسفة Next.js في تبسيط دورة حياة التطوير والنشر، مما يجعله خيارًا ممتازًا للمطورين الذين يسعون لتقديم تجارب مستخدم سريعة وغنية بالمحتوى.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *