מבוא זריז ל Next.JS

20/06/2021

אחד הפרויקטים המעניינים והפופולריים בעולם של React היום הוא Next.JS. הפרויקט מציע פריימוורק לפיתוח קוד Front End ו Back End משולבים ומטפל בשבילנו באינטגרציה בין השניים. בואו נראה איך זה עובד ומתי כדאי להשתמש בו.

1. מה זה Next.JS

נקסט הוא משהו בין אוסף כלים לפריימוורק שעוזר לפתח יישומי React הכוללים רכיב של Backend for Front End, כלומר בנוסף לקוד צד הלקוח הרגיל יש לנו גם קוד צד שרת שאחראי בעיקר על תקשורת ושליפת מידע מיישום או יישומים גדולים יותר.

נדמיין מערכת שחושפת כבר מספר API Endpoints או אולי אפילו מספר מערכות קיימות, ועכשיו אנחנו רוצים להוסיף ממשק ריאקטי למערכת זו. למרות שאני יכול להשתמש ב JavaScript בצד לקוח כדי למשוך את כל המידע שאני רוצה מהמערכות הישנות, יכול להיות שזה יוביל להרבה בקשות Ajax. שימוש בשרת Node.JS קטן שיפנה לכל המערכות הישנות ובמכה אחת יוציא את כל המידע וישלח אותו לריאקט יכול לשפר ביצועים בצורה משמעותית.

ייתרון נוסף של הקמת Node.JS Backend היא התמיכה ב Server Side Rendering. מנגנון מובנה בפריימוורק מאפשר לשרת להחזיר ללקוחות קוד HTML מוכן להצגה במקום קוד JavaScript וכך לשפר את חווית המשתמש וגם לשפר את הניראות במנועי חיפוש.

יכולת נוספת מעניינת של נקסט היא מנגנון ה Build לפרויקט. מנגנון זה יכול לייצר אתר סטטי מתוך קוד React. המנגנון מגיע עם יכולת להריץ קוד שלנו בזמן הבניה, וכך אפשר לקחת לדוגמה אתר וורדפרס שלם ולהפוך אותו לאתר סטטי, כלומר לבטל את התלות בבסיס הנתונים.

סך הכל Next יעזור לנו לבנות אתר ריאקט באחד או יותר מהתרחישים הבאים:

  1. כשאנחנו רוצים לבנות אתר סטטי לגמרי אבל תוך שימוש ב React ואפשרות להריץ קוד JavaScript בזמן הבניה.

  2. כשאנחנו רוצים לבנות אתר צד-לקוח שמתממשק עם שרת Node.JS ומחפשים מנגנון לשיתוף קוד פשוט בין השניים.

  3. כשאנחנו רוצים לבנות אתר React עם Server Side Rendering.

2. מבנה פרויקט

בשביל להתחיל פרויקט נקסט מפעילים את הפקודה הבאה:

$ npx create-next-app <app_name>

לדוגמה אם שם האפליקציה שלי הוא frontend אני מפעיל:

$ npx create-next-app frontend

הפקודה יוצרת תיקיה בשם frontend ובתוכה התיקיות:

.
├── next.config.js
├── package.json
├── pages
│   ├── api
│   │   └── hello.js
│   ├── _app.js
│   └── index.js
├── public
│   ├── favicon.ico
│   └── vercel.svg
├── README.md
├── styles
│   ├── globals.css
│   └── Home.module.css
└── yarn.lock

בשביל להריץ את הפרויקט במצב פיתוח מפעילים:

$ yarn dev

הקוד לסטארטר מתחיל בקובץ pages/index.js. אתם יכולים להיכנס ולשנות אותו ולראות על המסך בלייב את העדכונים שלכם.

זה קוד הסטארטר כמו שנוצר אצלי:

import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'

export default function Home() {
  return (
    <div className={styles.container}>
      <Head>
        <title>Create Next App</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        <h1 className={styles.title}>
          Welcome to <a href="https://nextjs.org">Next.js!</a>
        </h1>

        <p className={styles.description}>
          Get started by editing{' '}
          <code className={styles.code}>pages/index.js</code>
        </p>

        <div className={styles.grid}>
          <a href="https://nextjs.org/docs" className={styles.card}>
            <h2>Documentation &rarr;</h2>
            <p>Find in-depth information about Next.js features and API.</p>
          </a>

          <a href="https://nextjs.org/learn" className={styles.card}>
            <h2>Learn &rarr;</h2>
            <p>Learn about Next.js in an interactive course with quizzes!</p>
          </a>

          <a
            href="https://github.com/vercel/next.js/tree/master/examples"
            className={styles.card}
          >
            <h2>Examples &rarr;</h2>
            <p>Discover and deploy boilerplate example Next.js projects.</p>
          </a>

          <a
            href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
            className={styles.card}
          >
            <h2>Deploy &rarr;</h2>
            <p>
              Instantly deploy your Next.js site to a public URL with Vercel.
            </p>
          </a>
        </div>
      </main>

      <footer className={styles.footer}>
        <a
          href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
          target="_blank"
          rel="noopener noreferrer"
        >
          Powered by{' '}
          <span className={styles.logo}>
            <Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
          </span>
        </a>
      </footer>
    </div>
  )
}

הפרויקט כבר כולל מערכת הגדרות וובפאק די מורכבת שכרגיל מוחבאת מאיתנו. אנחנו יכולים להוסיף הגדרות משלנו לקובץ next.config.js, למשל בשביל להוסיף Loader-ים. בין הדברים שכבר קיבלנו מהקופסא אפשר למצוא את התמיכה ב CSS Modules, טעינת קבצי CSS ותמיכה ב Scss.

לפני שנמשיך לבנות עוד דפים בואו נראה איך לייצא את מה שכבר יש לנו לתיקיית out כדי לקבל אתר סטטי. נקסט מגיע עם מנגנון לאופטימיזציה של תמונות באמצעות הקומפוננטה Image, אבל מגננון זה בברירת מחדל לא נתמך באתרים סטטיים (יש להם כמה וורקאראונדים אבל לא ניכנס לזה כאן), לכן קודם כל נחליף את Image ב img רגיל בקובץ pages/index.js ונקבל בקובץ את התוכן הבא:

import Head from 'next/head'
import styles from '../styles/Home.module.css'

export default function Home() {
  return (
    <div className={styles.container}>
      <Head>
        <title>Create Next App</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        <h1 className={styles.title}>
          Welcome to <a href="https://nextjs.org">Next.js!</a>
        </h1>

        <p className={styles.description}>
          Get started by editing{' '}
          <code className={styles.code}>pages/index.js</code>
        </p>

        <div className={styles.grid}>
          <a href="https://nextjs.org/docs" className={styles.card}>
            <h2>Documentation &rarr;</h2>
            <p>Find in-depth information about Next.js features and API.</p>
          </a>

          <a href="https://nextjs.org/learn" className={styles.card}>
            <h2>Learn &rarr;</h2>
            <p>Learn about Next.js in an interactive course with quizzes!</p>
          </a>

          <a
            href="https://github.com/vercel/next.js/tree/master/examples"
            className={styles.card}
          >
            <h2>Examples &rarr;</h2>
            <p>Discover and deploy boilerplate example Next.js projects.</p>
          </a>

          <a
            href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
            className={styles.card}
          >
            <h2>Deploy &rarr;</h2>
            <p>
              Instantly deploy your Next.js site to a public URL with Vercel.
            </p>
          </a>
        </div>
      </main>

      <footer className={styles.footer}>
        <a
          href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
          target="_blank"
          rel="noopener noreferrer"
        >
          Powered by{' '}
          <span className={styles.logo}>
            <img src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
          </span>
        </a>
      </footer>
    </div>
  )
}

לצערי זה לא מספיק כי נקסט ממש רוצים שנשתמש באחד ממנגנוני אופטימיזציית התמונות שלהם, אז צריך גם לעדכן את הקובץ .eslintrc לתוכן הבא:

{
  "extends": ["next", "next/core-web-vitals"],
  "rules": {
    "@next/next/no-img-element": "off",
  }
}

ועכשיו אפשר להיכנס לתיקיית הפרויקט ולהפעיל:

$ npx next build && npx next export

ולקבל בתיקיית out את כל הפרויקט בתור קבצים סטטיים.

3. מערכת הניתובים

אחרי שראינו איך להשתמש ב Next כדי לבנות אתר סטטי בואו נמשיך לראות איך להשתמש בו כדי לבנות אתר דינמי עם Server Side Rendering ו Client Side Router. האמת שזה די פשוט.

בתיקיית pages יש לנו בינתיים דף אחד בקובץ index.js. כל קובץ שאנחנו יוצרים שם מייצג דף נוסף באתר, ובאופן אוטומטי מקבל חיבור ל Router. ניצור קובץ נוסף באותה תיקיה בשם about.js ובו התוכן הבא:

import Head from 'next/head'
import Link from 'next/link'

export default function About() {
  return (
    <div>
      <Head>
        <title>About Us</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main>
        <h1>About Us</h1>
        <p>
          Next.JS is actually a cool project.
          <Link href="/">Back Home</Link>
        </p>
      </main>
    </div>
  )
}

ובקובץ index.js הוסיפו איפה שתרצו את הלינק לדף האודות:

<Link href="/about">About Us</Link>

הפעלה מחדש במצב פיתוח עם yarn dev תאפשר לנו לעבור בין הדפים או לטעון כל אחד מהם בנפרד. במצב ייצור כל נתיב נשמר בקובץ js משלו ובצורה אוטומטית רק במעבר בין דפים ייטען הקוד של הדפים הפנימיים (מה שנקרא Code Splitting). אם תלחצו על View Page Source בדפדפן תוכלו לראות שמקור הדף הוא HTML מלא. זה בעצם אותו הדף שרונדר בשלב הבניה של הפרויקט.

לנקסט יש מנגנון שמאפשר הרצת קוד צד-שרת עם כל בקשה נכנסת ורינדור העמוד בשרת בצורה דינמית באמצעות פונקציה בשם getServerSideProps. אפשר לקרוא עליה בתיעוד כאן: https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering.

4. פיתוח API Endpoint

ה Use Case השלישי של נקסט הוא הקמת Backend for Frontend, כלומר שרת Node.JS חביב שנכתב יחד עם האפליקציה שלנו ותפקידו למשוך מידע ממערכות ישנות ולחבר מספר מקורות מידע שונים כדי שיהיה לי קל לכתוב את קוד ה Front End.

ב Next אני בונה API Endpoints כאלה באמצעות יצירת קבצים בתיקיית pages/api. נראה את קובץ הדוגמה שנמצא שם כבר בשם pages/api/hello.js:

// Next.js API route support: https://nextjs.org/docs/api-routes/introduction

export default function handler(req, res) {
  res.status(200).json({ name: 'John Doe' })
}

הקובץ מייצא פונקציה בודדת בשם handler המקבלת אוביקט Request ו Response ושולחת אוביקט JSON ללקוח.

בואו נעדכן את דף ה"אודות" שיצרנו כך שימשוך את המידע מה API הזה. קודם כל אני מוסיף לפרויקט את swr עם:

$ yarn add swr

ונעדכן את הקוד בקובץ pages/about.js לתוכן הבא:

import Head from 'next/head'
import Link from 'next/link'
import useSWR from 'swr'

const fetcher = (...args) => fetch(...args).then(res => res.json())

export default function About() {
  const { data, error } = useSWR('/api/hello', fetcher)
  const name = data ? data.name : undefined;

  return (
    <div>
      <Head>
        <title>About Us</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main>
        <h1>About Us</h1>
        {name && <h2>Hello {name}</h2>}
        <p>
          Next.JS is actually a cool project.
          <Link href="/">Back Home</Link>
        </p>
      </main>
    </div>
  )
}

שימו לב לשורה:

const { data, error } = useSWR('/api/hello', fetcher)

באופן אוטומטי יש לנו נתיב בצד השרת שמתאים לקובץ api/hello.js, שנטען מאותו Origin כך שלא צריך מדיניות CORS ומריץ את הפונקציה handler.

יש להם גם תמיכה מלאה בנתיבים דינמיים ופרמטרים ואפשר לקרוא על זה בתיעוד כאן: https://nextjs.org/docs/api-routes/dynamic-api-routes.

5. דעתי על הפרויקט

נקסט הוא פרויקט גדול עם המון יכולות והמון פינוקים קטנים. בעבודה איתו עבור ה Use Cases שתיארתי בפוסט כמעט לא בזבזתי זמן על חיפוש פיתרונות ולא ראיתי מקומות בהם נקסט הכריח אותי לעבוד בצורה מסוימת או חסם אותי כך שלא יכולתי לפתור בעיה בצורה שרציתי.

סך הכל מאוד אהבתי את הפרויקט ואם אתם מתחילים פרויקט ריאקט חדש בקרוב אני ממליץ לבדוק אותו בתור תחליף מעניין ל create-react-app.