الانتقال إلى Next.js

من موسوعة حسوب

صُمِّمت Next.js لكي يجري تبنيها تدريجيًا، إذ ستكون قادرًا على متابعة العمل على شيفرة جاهزة حالية وإضافة المقدار الذي تريده من شيفرة React. فإن بدأت بكمية صغيرة من الشيفرة ووسعتها تدريجيًا بإضافة صفحات جديدة، سيسهل ذلك عليك تبني Next.js في مشاريعك وتجنب إعادة كتابة كل شيء بالكامل.

استراتيجيات العمل

المسارات الفرعية Subpath

تقتضي الاستراتيجية الأولى بتهيئة الخادم أو الخادم الوكيل لوضع كل ما يتعلق بتطبيق Next.js في نفس المسار الفرعي (ضمن مجلد فرعي). فقد يكون موقعك الإلكتروني مثلًا example.com ومن ثم تهيئ المسار الفرعي example.com/store على الخادم ليضم تطبيق لمتجر إلكتروني.

بإمكانك تهيئة أصول assets وروابط تطبيق Next.js لتعمل تلقائيًا عند طلب الوجهة store/، وذلك باستخدام basePath. وبما أن كل صفحة من صفحات Next.js هي وجهة قائمة بنفسها، يمكنك الوصول إلى الصفحة pages/products.js مثلًا عبر المسار example.com/store/products.

// next.config.js

module.exports = {
  basePath: '/store',
}

الدالة rewrites

تقتضي الاستراتيجية الثانية إنشاء تطبيق Next.js جديد يشير إلى عنوان URL الجذري للنطاق domain الذي تستخدمه. استخدم بعد ذلك الدالة rewrites ضمن ملف التهيئة لكي تُحوَّل بعض المسارات إلى التطبيق الموجود.


لنفرض مثلًا أنك أنشأت تطبيق Next.js كي يُخدّم عبر النطاق example.com وله ملف التهيئة next.config.js. عندها ستتعامل Next.js مع الصفحات التي أضفتها إلى تطبيقك (مثل about/ إن أضفت pages/about.js)، بينما ستحوّل أية طلبات أخرى (مثل dashboard/) إلى proxy.example.com.

ملاحظة: إن كنت تستعمل fallback: true/'blocking'‎ في getStaticPaths، فلن يعمل الالتقاط الكامل catch-all ضمن rewrites المُعرف في الملف next.config.js إذ ستلتقطها getStaticPaths.

// next.config.js

module.exports = {
  async rewrites() {
    return {
        
      //(بما في ذلك المسارت الديناميكة) Next.js بعد التحقق من كل صفحات
      // والملفات الساكنة سنحوّل أي طلبات أخرى
      fallback: [
        {
          source: '/:path*',
          destination: `https://proxy.example.com/:path*`,
        },
      ],
    }
    //في الإصدارات مادون 10.1 rewrite يمكنك استخدام الدالة غير الفعالة 
    return [
      // لابد من تعريف دالة غير فعالة لتحرض عملية التحقق
      // من كل الصفحات الساكنة قبل أن نحاول تحويل المسار
      {
        source: '/:path*',
        destination: '/:path*',
      },
      {
        source: '/:path*',
        destination: `https://proxy.example.com/:path*`,
      },
    ]
  },
}

ملاحظة: إن كنت تنتقل تدريجيا إلى الوجهات الديناميكية مثل ‎[slug] وكنت تستعمل fallback: true أو fallback: 'blocking'‎ مع rewrite، فتأكد من أن تشمل حالة عدم العثور على صفحة page not found، إذ عندما تلتقط Next.js الوجهات الديناميكية فإنها تتوقف عن التحقق من أي وجهات أخرى. استعمال notFound: true في getStaticProps سيعيد صفحة 404 دون تطبيق rewrite، فإن لم تكن ترغب بذلك، يمكنك استعمال getServerSideProps مع ترويسة التحكم بالتخزين المؤقت stale-while-revalidate عند إعادة الخاصيات props وبعدها يمكنك إعادة التوجيه proxy يدويًا إلى الواجهة الخلفية الحالية باستعمال أدوات مثل http-proxy بدلًا من إعادة notFound: true.

واجهات أمامية مُصغرة مع مستودعات مفردة ونطاقات فرعية

تجعل Next.js و Vercel عملية بناء الواجهات الأمامية المصغّرة أو المجزّأة micro-frontends ونشرها إلى مستودع مفرد Monorepo أمرًا مباشرًا وبسيطًا. يسمح لك ذلك باستخدام النطاقات الفرعية لتبنّي تطبيقات جديدة تدريجيًا. ومن مزايا استخدام الواجهة الأمامية المصغّرة:

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

حالما تنهي تهيئة مستودعك المفرد، ادفع بالتغييرات إلى مستودع Git كالعادة وسترى أن حمولتك قد نُشرت ضمن مشاريع Vercel المرتبطة بها.

الانتقال من Gatesby

يساعدك هذا الدليل في نقل تطبيقك Gatsby إلى تطبيق Next.js؛ إذ يسمح لك الانتقال إلى Next.js أن:

سنحاول الآن إنجاز عملية الإنتقال خطوة خطوة

تحديث الملف package.json والاعتماديات

وهي الخطوة الأولى في عملية الانتقال وعليك تنفيذ ما يلي:

  • إزالة كل الحزم المتعلقة ب Gatsby (ابق فقط react و react-dom)
  • ثبِّت next.
  • أضف الأوامر Next.js إلى scripts وأولها next dev الذي يشغّل خادم التطوير على العنوان localhost:3000. أما ثانيها فهو next build و next start لإنشاء وتشغيل نسخة الإنتاج.

إليك مثالًا عن package.json:

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start"
  },
  "dependencies": {
    "next": "latest",
    "react": "latest",
    "react-dom": "latest"
  }
}

الأصولو الساكنة والخرج المُصرَّف

تستخدم Gatsby المجلد public لوضع ملفات الخرج المصرَّفة بينما تستخدم Next.js هذا المجلد لوضع الأصول الساكنة static assets. إليك خطوات الانتقال في هذه المرحلة:

  • أزل الإشارة إلى المجلدين /cache. و public من الملف gitignore. واحذف المجلدين.
  • غيّر اسم المجلد static إلى public.
  • أضف next. إلى gitignore..

إنشاء الوجهات Routes

تدعم كلا Gatsby و Next المجلد pages الذي يستخدم أسلوب توجيه يعتمد على نظام الملفات. تستخدم المجلد src/pages الذي تدعمه أيضًا Next.js. تُنشئ Gatsby الوجهات الديناميكية باستخدام الواجهة البرمجية createPages ضمن الملف gatsby-node.js. ويمكنك في Next استخدام الوجهات الديناميكية ضمن المجلد pages للحصول على نفس التأثير. وبدلًا من وجود مجلد بالاسم template، يمكنك استخدام مكوّنات React ضمن ملف الوجهة الديناميكية:

  • في Gatsby: أنشئ مسارات ديناميكية لكل منشور مثلًا باستخدام الواجهة createPages ومن ثم أنشئ ملفًا يمثل قالبًا لهذه المنشورات ضمن src/templates/blog-post.js.
  • في Next: أنشئ المسارات الديناميكية على الشكل pages/blog/[slug].js لتمثل قالبًا للمنشورات. يمكن الوصول إلى قيمة slug من خلال معامل استعلام. إذ يمكن مثلًا الوصول إلى المنشور blog/first-post/ انطلاقًا من القالب pages/blog/[slug].js من خلال كائن الاستعلام { 'slug': 'first-post' }.

تنسيق الصفحات

تُدرج Gatsby تنسيقات CSS العامة في الملف gatsby-browser.js، لكن لا بد من إنشاء تطبيق app.js_ مخصص للتنسيقات العامة في Next.js. عند الانتقال إلى Next.js بإمكانك نسخ التنسيقات المُدرجة مباشرة وتحديث المسارات النسبية إن اقتضى الأمر. وتذكر أن Next.js تقدم دعمًا مدمجًا لتنسيق CSS.

الروابط

للمكوّن Link في Gatsby ونظيره Link في Next.js واجهتان برمجيتان تختلفان اختلافًا طفيفًا:

// Gatsby

import { Link } from 'gatsby'

export default function Home() {
  return <Link to="/blog">blog</Link>
}
// Next.js

import Link from 'next/link'

export default function Home() {
  return (
    <Link href="/blog">
      <a>blog</a>
    </Link>
  )
}

حدِّث أية عبارت إدراج مختلفة، ثم بدّل to إلى href وأضف العنصر <a> ضمن العنصر <link>.

إحضار البيانات

يظهر الاختلاف الجذري بين Gatsby و Next.js في طريقة إحضار البيانات. إذ يرتكز خيار Gatsby على "GraphQL" في إحضار البيانات إلى التطبيق، بينما عليك أن تختار في Next.js الاستراتيجية التي تريد ومن ضمنها "GraphQL".

تستخدم Gatsby الدالة graphql لاستعلام البيانات في صفحات الموقع بما فيها البيانات المحلية والبعيدة ومعلومات التهيئة الخاصة بالموقع، كما تسمح بإنشاء صفحات ساكنة فقط. أما في Next.js، بإمكانك اختيار استراتيجية إحضار البيانات الخاصة بكل صفحة. إذ تسمح لك الدالة getServerSideProps مثلًا بتنفيذ التصيير من جانب الخادم، بينما بإمكانك تصدير الدالتين getStaticProps / getStaticPaths ضمن الصفحة لتوليد صفحات ساكنة بدلًا من تنفيذ pageQuery. إليك مثالًا:

// src/pages/[slug].js
// remark-html و remark ثبِّت 
import { remark } from 'remark'
import html from 'remark-html'
import { getPostBySlug, getAllPosts } from '../lib/blog'

export async function getStaticProps({ params }) {
  const post = getPostBySlug(params.slug)
  const markdown = await remark()
    .use(html)
    .process(post.content || '')
  const content = markdown.toString()

  return {
    props: {
      ...post,
      content,
    },
  }
}

export async function getStaticPaths() {
  const posts = getAllPosts()

  return {
    paths: posts.map((post) => {
      return {
        params: {
          slug: post.slug,
        },
      }
    }),
    fallback: false,
  }
}

سترى عادة ملحقات plugins في Gatsby لقراءة منظومة الملفات gatsby-source-filesystem أو التعامل مع تنسيق Markdown من خلال gatsby-transformer-remark وغيرها. فالمثال النموذجي الأكثر شعبية لمبتدئي تطبيقات المنشورات، قد استخدم 15 حزمة Gatsby خاصة. تأخذ Next في المقابل نهجًا آخر، إذ تُضمِّن الميزات كثيرة الاستعمال مباشرة ضمن إطار العمل، وتمنح المستخدم تحكمًا كاملًا في دعم التطبيق بحزم خارجية. فبدلًا من فصل شيفرة القراءة من منظومة الملفات ضمن ملحق، بإمكانك استخدام حزمة Node.js الأصلية fs ضمن الدالتين getStaticProps / getStaticPaths لقراءة منظومة الملفات:

// src/lib/blog.js
//date-fns و gray-matter ثبِّت 
import matter from 'gray-matter'
import { parseISO, format } from 'date-fns'
import fs from 'fs'
import { join } from 'path'
//`src/content/blog` إلى markdown أضف ملفات 
const postsDirectory = join(process.cwd(), 'src', 'content', 'blog')

export function getPostBySlug(slug) {
  const realSlug = slug.replace(/\.md$/, '')
  const fullPath = join(postsDirectory, `${realSlug}.md`)
  const fileContents = fs.readFileSync(fullPath, 'utf8')
  const { data, content } = matter(fileContents)
  const date = format(parseISO(data.date), 'MMMM dd, yyyy')

  return { slug: realSlug, frontmatter: { ...data, date }, content }
}

export function getAllPosts() {
  const slugs = fs.readdirSync(postsDirectory)
  const posts = slugs.map((slug) => getPostBySlug(slug))

  return posts
}

مكون الصور وتحسين الصور

تمتلك Next.js مكوِّن صور مدمج وطريقة تحسين مدمجة للصور. إذ يُعد المكوِّن next/image امتدادًا لعنصر الصورة <img> في HTML، وقد طوّر ليلائم مواقع الويب الحديثة.

يتيح لك التحسين التقائي للصور إعادة تحجيم الصورة وتحسينها وتقديمها بامتدادات حديثة مثل WebP عندما يدعمها المتصفح. يقلل ذلك من الدفع بصورة كبيرة إلى أجهزة لها نافذة عرض صغيرة، كما يسمح لـ Next.js بالتبني التلقائي لتنسيقات الصور المستقبلية، وتقديمها للمتصفح الذي يدعمها.

الإنتقال بالصور من Gatsby

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

  • gatsby-image
  • gatsby-transformer-sharp
  • gatsby-plugin-sharp

استخدم بدلًا من ذلك المكوّن next/image والتحسين التلقائي للصورة.

لا يُدعم المُحمّل الافتراضي للمكون next/image عند استخدام next export، لكن ستعمل خيارات أخرى.

import Image from 'next/image'
import profilePic from '../public/me.png'

export default function Home() {
  return (
    <>
      <h1>My Homepage</h1>
      <Image
        src={profilePic}
        alt="Picture of the author"
        //Gatsby في "fluid" الخيار "responsive" يُشابه 
        //Gatsby  بأعظم عرض في"fluid" الخيار "intrinsic" يُشابه 
        //Gatsby في ""fixed" الخيار "fixed" يُشابه 
        layout="responsive"
        //Gatsby في "blur-up" اختياري، ويشابه 
        placeholder="blur"
        //Gatsby GraphQL في "width" اختياري، ويشابه 
        width={500}
        //Gatsby GraphQL في "height" اختياري، ويشابه 
        height={500}
      />
      <p>Welcome to my homepage!</p>
    </>
  )
}

تهيئة الموقع

ستجد البيانات الوصفية (الاسم والوصف وغيرها) للموقع المبني باستخدام Gatsby ضمن الملف gatsby-config.js وتُستعرض بعد ذلك من قبل واجهة GraphQL البرمجية التي تتعامل معها من خلال pageQuery أو من خلال استعلام ساكن ضمن المكوّن.

لكن يُفضَّل في Next.js إنشاء ملف تهيئة يُشابه في محتواه الشيفرة التالية؛ ومن ثم تستطيع إدراج هذه الملف في أي مكان دون الحاجة لاستخدام GraphQL للولوج إلى البيانات الوصفية للموقع:

// src/config.js

export default {
  title: 'Starter Blog',
  author: {
    name: 'Lee Robinson',
    summary: 'who loves Next.js.',
  },
  description: 'A starter blog converting Gatsby -> Next.',
  social: {
    twitter: 'leeerob',
  },
}

تحسين نتائج البحث في محركات البحث

تستخدم معظم أمثلة Gatsby العنصر react-helmet للمساعدة في إضافة بيانات وصفية meta لأغراض تحسين نتيجة ظهور الموقع في محركات البحث SEO. تستخدم Next.js في المقابل المكوّن next/head لإضافة هذه البيانات إلى العنصر </ head>. إليك مثالًا عن مكوّن SEO في Gatsby:

// src/components/seo.js

import { Helmet } from 'react-helmet'

export default function SEO({ description, title, siteTitle }) {
  return (
    <Helmet
      title={title}
      titleTemplate={siteTitle ? `%s | ${siteTitle}` : null}
      meta={[
        {
          name: `description`,
          content: description,
        },
        {
          property: `og:title`,
          content: title,
        },
        {
          property: `og:description`,
          content: description,
        },
        {
          property: `og:type`,
          content: `website`,
        },
        {
          name: `twitter:card`,
          content: `summary`,
        },
        {
          name: `twitter:creator`,
          content: twitter,
        },
        {
          name: `twitter:title`,
          content: title,
        },
        {
          name: `twitter:description`,
          content: description,
        },
      ]}
    />
  )
}

وهذا هو المثال ذاته باستخدام Next.js بما في ذلك القراءة من ملف إعدادات الموقع:

// src/components/seo.js

import Head from 'next/head'
import config from '../config'

export default function SEO({ description, title }) {
  const siteTitle = config.title

  return (
    <Head>
      <title>{`${title} | ${siteTitle}`}</title>
      <meta name="description" content={description} />
      <meta property="og:type" content="website" />
      <meta property="og:title" content={title} />
      <meta property="og:description" content={description} />
      <meta property="og:site_name" content={siteTitle} />
      <meta property="twitter:card" content="summary" />
      <meta property="twitter:creator" content={config.social.twitter} />
      <meta property="twitter:title" content={title} />
      <meta property="twitter:description" content={description} />
    </Head>
  )
}

الانتقال من Create React App

يساعدك هذا الدليل في نقل تطبيقك من Create React App إلى تطبيق Next.js؛ إذ يسمح لك ذلك أن:

سنحاول الآن إنجاز عملية الإنتقال خطوة خطوة.

تحديث الملف package.json والاعتماديات

وهي الخطوة الأولى في عملية الانتقال وعليك تنفيذ ما يلي:

  • إزالة حزم react (ابق فقط react و react-dom). إن كنت تستخدم React Router يمكنك إزالة react-router-dom أيضًا.
  • ثبِّت next.
  • أضف أوامر Next.js إلى scripts وأولها next dev الذي يشغّل خادم التطوير على العنوان localhost:3000. أما ثانيها فهو next build و next start لإنشاء وتشغيل نسخة الإنتاج.

إليك مثالًا عن package.json:

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start"
  },
  "dependencies": {
    "next": "latest",
    "react": "latest",
    "react-dom": "latest"
  }
}

الأصول الساكنة والخرج المُصرَّف

تستخدم Create React App المجلد public لوضع ملفات HTML التي تُمثِّل نقاط دخول إلى التطبيق والموجودات الساكنة بينما تستخدم Next.js هذا المجلد لوضع الأصول الساكنة static assests فقط. إليك خطوات الانتقال في هذه المرحلة:

  • انقل أية صور أو خطوط أو أية الأصول أو موجودات ساكنة أخرى إلى المجلد public.
  • حوّل الملف index.html (نقطة الدخول إلى التطبيق) إلى Next.js، وينبغي نقل أية شيفرات <head> ضمنه إلى مستند مخصص document.js_، وكذلك أية تخطيطات layouts مشتركة بين الصفحات إلى تطبيق APP مخصص.
  • أضف next. إلى gitignore..

وسنناقش لاحقًا موضوع التنسيق.

إنشاء الوجهات والروابط

قد تستخدم المكتبة React Router في تطبيقات Create React App، لكن بدلًا من استخدام مكتبة من طرف آخر، تقدم طريقة Next.js آلية توجه مدمجة مبنية على نظام الملفات.

  • أنشئ المجلد pages في جذر المشروع.
  • انقل الملف src/App.js ليصبح pages/index.js. يمثل هذا الملف الصفحة الرئيسية لتطبيقك. ادمج هذا الملف مع الشيفرة التي تُستخدم لعرض وجهة الصفحة الرئيسية في تطبيق Create React App.
  • حوّل كل المكوّنات Route إلى ملفات جديدة في المجلد pages.
  • أما الوجهات التي تتطلب مساراتها محتوى ديناميكي (مثل blog/:slug/)، بإمكانك استخدام الوجهات الديناميكية في Next.js (مثل pages/blog/[slug].js). يمكن الوصول إلى قيمة slug من خلال معامل استعلام. إذ يمكن مثلًا الوصول إلى المنشور blog/first-post/ انطلاقًا من القالب pages/blog/[slug].js من خلال كائن الاستعلام { 'slug': 'first-post' }.

التنسيق

تقدم Next.js دعمًا مدمجًا لتنسيق CSS و SaSS و CSS-in-JS. كما يسمح Create React App بإدراج ملفات css. مباشرة ضمن المكوّنات. تسمح Next.js بنفس هذا الأسلوب أيضًا، لكن لا بد أن تكون هذه الملفات وحدات تنسيق CSS. أما بالنسبة للتنسيقات العامة، ستحتاج إلى تطبيق App مخصص لإضافتها.

الولوج الآمن إلى واجهة الويب البرمجية

يمكن الوصول إلى الواجهة window أو localStorage أو navigator وغيرها من واجهات ويب البرمجية مباشرة عبر التطبيقات التي تُصيَّر من جانب العميل. لكن طالما أن Next.js تستخدم التصيير المسبق، لا بد من الوصول إلى تلك الواجهات بطريقة آمنة عندما يكون المستخدم في الواجهة الأمامية. تسمح الشيفرة التالية مثلًا بالوصول إلى الواجهة window من جهة العميل فقط (من الواجهة الأمامية):

if (typeof window !== 'undefined') {
  //الآن window يمكنك الوصول إلى `
}

ومن الطرق المستحسنة للوصول الآمن إلى الواجهات هو استخدام الخطاف useEffect والذي يُنفَّذ فقط في جانب العميل:

import { useEffect } from 'react'

useEffect(() => {
  //الآن window يمكنك الوصول إلى `
}, [])

مكوّن الصور وتحسين الصور

تمتلك Next.js ابتداءً من الإصدار 10.0.0 مكوِّن صور مدمج وطريقة تحسين مدمجة للصور. إذ يُعد المكوَّن next/image امتدادًا لعنصر الصورة <img> في HTML، وقد طوّر ليلائم مواقع الويب الحديثة.

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

تعمل Next.js على تحسين الصورة عند الطلب بدلًا من تحسينها أثناء بناء التطبيق. وعلى خلاف مولِّدات المواقع الساكنة والحلول الساكنة فقط، لن يزيد ذلك زمن بناء التطبيق سواءً حمّلت 10 صور أو 10 ملايين صورة.

import Image from 'next/image'

export default function Home() {
  return (
    <>
      <h1>My Homepage</h1>
      <Image
        src="/me.png"
        alt="Picture of the author"
        width={500}
        height={500}
      />
      <p>Welcome to my homepage!</p>
    </>
  )
}

متغيرات البيئة

تدعم متغيرات البيئة env. كما يفعل Create React App، ويكمن الفرق الأساسي بينهما هو البادئة التي تكشف متغير البيئة لجانب العميل.

  • حوّل بادئات متغيرات البيئة من _REACT_APP إلى _NEXT_PUBLIC.
  • تتاح متغيرات البيئة للاستخدام أثناء بناء التطبيق وعند طلب وجهات API.

تحسين نتائج البحث في محركات البحث

تستخدم معظم أمثلة Create React App العنصر react-helmet للمساعدة في إضافة بيانات وصفية meta لأغراض تحسين نتيجة ظهور الموقع في محركات البحث SEO. تستخدم Next.js في المقابل المكوّن next/head لإضافة هذه البيانات إلى العنصر </ head>. إليك مثالًا عن مكوّن SEO في تطبيق Create React App:

// src/components/seo.js

import { Helmet } from 'react-helmet'

export default function SEO({ description, title, siteTitle }) {
  return (
    <Helmet
      title={title}
      titleTemplate={siteTitle ? `%s | ${siteTitle}` : null}
      meta={[
        {
          name: `description`,
          content: description,
        },
        {
          property: `og:title`,
          content: title,
        },
        {
          property: `og:description`,
          content: description,
        },
        {
          property: `og:type`,
          content: `website`,
        },
        {
          name: `twitter:card`,
          content: `summary`,
        },
        {
          name: `twitter:creator`,
          content: twitter,
        },
        {
          name: `twitter:title`,
          content: title,
        },
        {
          name: `twitter:description`,
          content: description,
        },
      ]}
    />
  )
}

وهذا هو المثال ذاته باستخدام Next.js:

// src/components/seo.js

import Head from 'next/head'

export default function SEO({ description, title, siteTitle }) {
  return (
    <Head>
      <title>{`${title} | ${siteTitle}`}</title>
      <meta name="description" content={description} />
      <meta property="og:type" content="website" />
      <meta property="og:title" content={title} />
      <meta property="og:description" content={description} />
      <meta property="og:site_name" content={siteTitle} />
      <meta property="twitter:card" content="summary" />
      <meta property="twitter:creator" content={config.social.twitter} />
      <meta property="twitter:title" content={title} />
      <meta property="twitter:description" content={description} />
    </Head>
  )
}

تطبيقات الصفحة الواحدة

إن أردت نقل تطبيق Create React App إلى Next.js والإبقاء على تطبيق الصفحة الواحدة، انقل جميع نقاط الدخول إلى التطبيق إلى مسار التقاط اختياري لأي وجهة يُدعى pages/[[...app]].js.

// pages/[[...app]].js

import { useState, useEffect } from 'react'
import CreateReactAppEntryPoint from '../components/app'

function App() {
  const [isMounted, setIsMounted] = useState(false)

  useEffect(() => {
    setIsMounted(true)
  }, [])

  if (!isMounted) {
    return null
  }

  return <CreateReactAppEntryPoint />
}

export default App

التطبيقات المدارة ذاتيًا

إن كنت تمتلك تطبيق Create React App مُدار ذاتيًا (أو مفصول ejected باستعمال الأمر npm run eject وتديره أنت ذاتيًا بكامل ضبطه وخياراته)، إليك بعض النقاط التي ينبغي أخذها بعين الاعتبار:

  • إن كان لديك ملف مخصص مهيأ ليضم محمِّلات CSS أو Sass أو غيرها من الموجودات فهذه المحملات مدمجة في Next.js.
  • إن أضفت بنفسك ميزات JavaScript جديدة (كالسَلسَلة الاختيارية) أو شيفرات مواءمة Polyfills، فتحقق مما هو مدمج بالفعل في Next.js.
  • إن كان لديك إعدادات مخصصة لفصل الشيفرات Next.js، يمكنك إزالتها لأن Next.js تمتلك آلية فصل شيفرات تلقائية مبنية على أساس الصفحات.
  • يمكنك تخصيص إعدادات PostCSS في Next.js دون الحاجة إلى فصل التطبيق عن منصة العمل.
  • عليك العودة إلى الإعدادات الافتراضية لمفسّر Babel ومحزّم Webpack في Next.js لتقف على ما هو موجود افتراضيًا.

الانتقال من React Router

يساعدك هذا الدليل في فهم آلية الانتقال من استخدام المكتبة React Router في تحديد الوجهات إلى الاعتماد على منظومة الملفات. تستطيع أن تستخدم في Next.js المكونين next/link و next/router لكي :

  • تقلل حجم تجميعة الشيفرة بإزالة الاعتمادية React Router.
  • تحديد الوجهات في تطبيقك من خلال منظومة الملفات.
  • الاستفادة من آخر التحسينات في إطار عمل Next.js.

الأساسيات

ألغ تثبيت React Router أولًا إذ سننتقل إلى استخدام طريقة التوجيه المدمجة مع Next.js:

npm uninstall react-router-dom

يختلف المكوّن Link الذي ينفّذ عمليات التنقل في جانب العميل قليلًا عن React Router:

// قبل (React Router)
import { Link } from 'react-router-dom'

export default function App() {
  return <Link to="/about">About</Link>
}

// يعد (Next.js)
import Link from 'next/link'

export default function App() {
  return (
    <Link href="/about">
      <a>About</a>
    </Link>
  )
}

تمتلك معظم تطبيقات React التي تستخدم React Router ملفًا عال المستوى يحتوي على قائمة بكل الوجهات. إليك مثالًا:

import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'

export default function App() {
  return (
    <Router>
      <Switch>
        <Route path="/about">
          <h1>About</h1>
        </Route>
        <Route path="/blog">
          <h1>Blog</h1>
        </Route>
        <Route path="/">
          <h1>Home</h1>
        </Route>
      </Switch>
    </Router>
  )
}

يمكنك التعبير عن نفس هيكيلية التطبيق في Next.js باستخدام منظومة الملفات. فعندما يُضاف ملف إلى المجلد pagesسيكون متاحًا كوجهة:

  • pages/about.js/about
  • pages/blog.js/blog
  • pages/index.js/

الوجهات المتداخلة

تُصيّر الوجهات مثل blog/my-post/ في الشيفرة التالية إلى مكوِّن Post. فإن لم تكن هناك كتلة ديناميكية slug تحدد هوية المنشور، سيصيّر قائمة المنشورات بأكملها:

import {
  BrowserRouter as Router,
  Switch,
  Route,
  useRouteMatch,
  useParams,
} from 'react-router-dom'

export default function Blog() {
  // Nested route under /blog
  const match = useRouteMatch()

  return (
    <Router>
      <Switch>
        <Route path={`${match.path}/:slug`}>
          <Post />
        </Route>
        <Route path={match.path}>
          <h1>All Blog Posts</h1>
        </Route>
      </Switch>
    </Router>
  )
}

function Post() {
  const { slug } = useParams()
  return <h1>Post Slug: {slug}</h1>
}

تستخدم Next.js الصيغة [slug:] ضمن اسم الملف للتوجه الديناميكي بدلًا من استخدام slug: ضمن المكوّن Route. وبالإمكان الانتقال إلى Next.js بإنشاء ملفين جديدين الأول pages/blog/[slug].js (ليعرض كل الصفحات) والآخر pages/blog/[slug].js (لعرض منشورات مفردة):

// pages/blog/index.js

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

// pages/blog/[slug].js

import { useRouter } from 'next/router'

export default function Post() {
  const router = useRouter()
  const { slug } = router.query

  return <h1>Post Slug: {slug}</h1>
}

التصيير من جانب الخادم

تقدم Next.js دعمًا مدمجًا للتصيير من جانب الخادم. أي بالإمكان إزالة أي نسخ عن StaticRouter في الشيفرة.

فصل الشيفرة

تقدم Next.js دعمًا مدمجًا لفصل الشيفرات، أي بالإمكان إزالة أية نسخ عن:

  • loadable/server@ و loadable/babel-plugin@ و loadable/webpack-plugin@
  • أية تعديلات على الملف babelrc. لأجل loadable/babel-plugin@

ستُفصل شيفرة كل ملف موجود في المجلد pages إلى تجميعة JavaScript خاصة خلال عملية البناء. كما تدعم Next.js الإدراج الديناميكي ()import وفق مواصفات JavaScript إصدار ES2020. وهكذا يمكنك إدراج وحدات JavaScript ديناميكيًا والعمل عليها. كما تعمل أيضًا مع التصيير من جانب الخادم.

راجع صفحة الإدراج الديناميكي لمعلومات أوفى.

استعادة موضع التمرير

تقدم Next.js دعمًا مدمجًا لاستعادة موضع التمرير scroll restoration، وبالتالي يمكنك إزالة أية مكوّنات ScrollToTop خاصة قد عرّفتها.

إن السلوك الافتراضي للمكونين next/link و next/router هو تمرير المحتوى حتى أعلى الصفحة، لكن بإمكانك أيضًا تعطيل هذا السلوك إن أردت.

أمثلة

اقرأ أيضًا

المصادر