עברית

גלו את העוצמה של ה-App Router ב-Next.js עם המדריך המעמיק שלנו לניתוב מבוסס קבצים. למדו כיצד לבנות את האפליקציה, ליצור נתיבים דינמיים, לנהל פריסות ועוד.

Next.js App Router: מדריך מקיף לניתוב מבוסס קבצים

ה-App Router של Next.js, שהוצג ב-Next.js 13 והפך לסטנדרט בגרסאות מאוחרות יותר, מחולל מהפכה בדרך שבה אנו בונים ומנווטים באפליקציות. הוא מציג מערכת ניתוב עוצמתית ואינטואיטיבית מבוססת קבצים שמפשטת את הפיתוח, משפרת את הביצועים ומשדרגת את חוויית המפתחים הכוללת. מדריך מקיף זה יצלול לעומק הניתוב מבוסס הקבצים של ה-App Router, ויספק לכם את הידע והמיומנויות לבנות אפליקציות Next.js חזקות וניתנות להרחבה.

מהו ניתוב מבוסס קבצים?

ניתוב מבוסס קבצים הוא מערכת ניתוב שבה מבנה הנתיבים של האפליקציה שלכם נקבע ישירות על ידי ארגון הקבצים והספריות. ב-App Router של Next.js, אתם מגדירים נתיבים על ידי יצירת קבצים בתוך ספריית ה-`app`. כל תיקייה מייצגת מקטע נתיב, וקבצים מיוחדים בתוך התיקיות הללו מגדירים כיצד אותו מקטע נתיב יטופל. גישה זו מציעה מספר יתרונות:

צעדים ראשונים עם ה-App Router

כדי להשתמש ב-App Router, עליכם ליצור פרויקט Next.js חדש או להעביר פרויקט קיים. ודאו שאתם משתמשים ב-Next.js גרסה 13 ומעלה.

יצירת פרויקט חדש:

ניתן ליצור פרויקט Next.js חדש עם ה-App Router באמצעות הפקודה הבאה:

npx create-next-app@latest my-app --example with-app

העברת פרויקט קיים:

כדי להעביר פרויקט קיים, עליכם להעביר את הדפים שלכם מספריית `pages` לספריית `app`. ייתכן שתצטרכו להתאים את לוגיקת הניתוב שלכם בהתאם. Next.js מספק מדריך העברה שיעזור לכם בתהליך זה.

מושגי ליבה של ניתוב מבוסס קבצים

ה-App Router מציג מספר קבצים ומוסכמות מיוחדים המגדירים כיצד הנתיבים שלכם מטופלים:

1. ספריית `app`

ספריית ה-`app` היא השורש של נתיבי האפליקציה שלכם. כל הקבצים והתיקיות בתוך ספרייה זו ישמשו ליצירת נתיבים. כל דבר מחוץ לספריית ה-`app` (כמו ספריית `pages` אם אתם בתהליך העברה) לא יילקח בחשבון על ידי ה-App Router.

2. קובץ `page.js`

קובץ ה-`page.js` (או `page.jsx`, `page.ts`, `page.tsx`) הוא החלק הבסיסי ביותר של ה-App Router. הוא מגדיר את רכיב הממשק שיוצג עבור מקטע נתיב ספציפי. זהו קובץ **חובה** עבור כל מקטע נתיב שתרצו שיהיה נגיש ישירות.

דוגמה:

אם יש לכם מבנה קבצים כזה:

app/
  about/
    page.js

הרכיב המיוצא מ-`app/about/page.js` יוצג כאשר משתמש ינווט אל `/about`.

// app/about/page.js
import React from 'react';

export default function AboutPage() {
  return (
    <div>
      <h1>אודותינו</h1>
      <p>למדו עוד על החברה שלנו.</p>
    </div>
  );
}

3. קובץ `layout.js`

קובץ ה-`layout.js` (או `layout.jsx`, `layout.ts`, `layout.tsx`) מגדיר ממשק משתמש המשותף למספר דפים בתוך מקטע נתיב. פריסות (Layouts) שימושיות ליצירת כותרות עליונות (headers), כותרות תחתונות (footers), סרגלי צד ואלמנטים אחרים שאמורים להופיע במספר דפים.

דוגמה:

נניח שאתם רוצים להוסיף כותרת עליונה גם לדף `/about` וגם לדף היפותטי `/about/team`. ניתן ליצור קובץ `layout.js` בספריית `app/about`:

// app/about/layout.js
import React from 'react';

export default function AboutLayout({ children }) {
  return (
    <div>
      <header>
        <h1>אודות החברה שלנו</h1>
      </header>
      <main>{children}</main>
    </div>
  );
}

ה-prop `children` יוחלף בממשק המשתמש המוצג על ידי קובץ ה-`page.js` באותה ספרייה או בכל ספרייה מקוננת.

4. קובץ `template.js`

קובץ ה-`template.js` דומה ל-`layout.js`, אך הוא יוצר מופע חדש של הרכיב עבור כל נתיב-ילד. זה שימושי לתרחישים בהם רוצים לשמור על מצב הרכיב (component state) או למנוע רינדורים מחדש בעת ניווט בין נתיבי-ילד. בניגוד לפריסות, תבניות (templates) ירונדרו מחדש בניווט. שימוש בתבניות מצוין להנפשת אלמנטים בניווט.

דוגמה:

// app/template.js
'use client'

import { useState } from 'react'

export default function Template({ children }) {
  const [count, setCount] = useState(0)

  return (
    <main>
      <p>תבנית: {count}</p>
      <button onClick={() => setCount(count + 1)}>עדכן תבנית</button>
      {children}
    </main>
  )
}

5. קובץ `loading.js`

קובץ ה-`loading.js` (או `loading.jsx`, `loading.ts`, `loading.tsx`) מאפשר לכם ליצור ממשק טעינה המוצג בזמן שמקטע נתיב נטען. זה שימושי למתן חווית משתמש טובה יותר בעת שליפת נתונים או ביצוע פעולות אסינכרוניות אחרות.

דוגמה:

// app/about/loading.js
import React from 'react';

export default function Loading() {
  return <p>טוען מידע אודות...</p>;
}

כאשר משתמש מנווט ל-`/about`, רכיב ה-`Loading` יוצג עד שרכיב ה-`page.js` יוצג במלואו.

6. קובץ `error.js`

קובץ ה-`error.js` (או `error.jsx`, `error.ts`, `error.tsx`) מאפשר לכם ליצור ממשק שגיאה מותאם אישית המוצג כאשר מתרחשת שגיאה בתוך מקטע נתיב. זה שימושי למתן הודעת שגיאה ידידותית יותר למשתמש ולמניעת קריסת האפליקציה כולה.

דוגמה:

// app/about/error.js
'use client'

import React from 'react';

export default function Error({ error, reset }) {
  return (
    <div>
      <h2>אירעה שגיאה!</h2>
      <p>{error.message}</p>
      <button onClick={() => reset()}>נסה שוב</button>
    </div>
  );
}

אם תתרחש שגיאה בעת רינדור הדף `/about`, רכיב ה-`Error` יוצג. ה-prop `error` מכיל מידע על השגיאה, והפונקציה `reset` מאפשרת למשתמש לנסות לטעון מחדש את הדף.

7. קבוצות נתיבים (Route Groups)

קבוצות נתיבים `(groupName)` מאפשרות לכם לארגן את הנתיבים שלכם מבלי להשפיע על מבנה ה-URL. הן נוצרות על ידי עטיפת שם תיקייה בסוגריים. זה שימושי במיוחד לארגון פריסות ורכיבים משותפים.

דוגמה:

app/
  (marketing)/
    about/
      page.js
    contact/
      page.js
  (shop)/
    products/
      page.js

בדוגמה זו, דפי `about` ו-`contact` מקובצים תחת קבוצת `marketing`, ודף `products` נמצא תחת קבוצת `shop`. כתובות ה-URL נשארות `/about`, `/contact` ו-`/products` בהתאמה.

8. נתיבים דינמיים

נתיבים דינמיים מאפשרים לכם ליצור נתיבים עם מקטעים משתנים. זה שימושי להצגת תוכן המבוסס על נתונים שנשלפו ממסד נתונים או מ-API. מקטעי נתיב דינמיים מוגדרים על ידי עטיפת שם המקטע בסוגריים מרובעים (למשל, `[id]`).

דוגמה:

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

app/
  blog/
    [id]/
      page.js

מקטע ה-`[id]` הוא מקטע דינמי. הרכיב המיוצא מ-`app/blog/[id]/page.js` יוצג כאשר משתמש ינווט לכתובת URL כמו `/blog/123` או `/blog/456`. ערך הפרמטר `id` יהיה זמין ב-prop `params` של הרכיב.

// app/blog/[id]/page.js
import React from 'react';

export default async function BlogPost({ params }) {
  const { id } = params;

  // שליפת נתונים עבור פוסט הבלוג עם ה-ID הנתון
  const post = await fetchBlogPost(id);

  if (!post) {
    return <p>פוסט בלוג לא נמצא.</p>;
  }

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

async function fetchBlogPost(id) {
  // הדמיה של שליפת נתונים ממסד נתונים או API
  return new Promise((resolve) => {
    setTimeout(() => {
      const posts = {
        '123': { title: 'הפוסט הראשון שלי בבלוג', content: 'זהו התוכן של הפוסט הראשון שלי בבלוג.' },
        '456': { title: 'פוסט בלוג נוסף', content: 'זהו תוכן מרגש נוסף.' },
      };
      resolve(posts[id] || null);
    }, 500);
  });
}

ניתן גם להשתמש במספר מקטעים דינמיים בנתיב. לדוגמה, יכול להיות לכם נתיב כמו `/blog/[category]/[id]`.

9. מקטעי Catch-all

מקטעי Catch-all מאפשרים לכם ליצור נתיבים התואמים למספר כלשהו של מקטעים. זה שימושי לתרחישים כמו יצירת CMS שבו מבנה ה-URL נקבע על ידי המשתמש. מקטעי Catch-all מוגדרים על ידי הוספת שלוש נקודות לפני שם המקטע (למשל, `[...slug]`).

דוגמה:

app/
  docs/
    [...slug]/
      page.js

מקטע ה-`[...slug]` יתאים לכל מספר של מקטעים אחרי `/docs`. לדוגמה, הוא יתאים ל-`/docs/getting-started`, `/docs/api/users`, ו-`/docs/advanced/configuration`. ערך הפרמטר `slug` יהיה מערך המכיל את המקטעים התואמים.

// app/docs/[...slug]/page.js
import React from 'react';

export default function DocsPage({ params }) {
  const { slug } = params;

  return (
    <div>
      <h1>מסמכים</h1>
      <p>Slug: {slug ? slug.join('/') : 'No slug'}</p>
    </div>
  );
}

ניתן ליצור מקטעי catch-all אופציונליים על ידי הוספת שם המקטע בסוגריים מרובעים כפולים `[[...slug]]`. זה הופך את מקטע הנתיב לאופציונלי. דוגמה:

app/
  blog/
    [[...slug]]/
      page.js

הגדרה זו תרנדר את הרכיב `page.js` גם ב-`/blog` וגם ב-`/blog/any/number/of/segments`.

10. נתיבים מקבילים (Parallel Routes)

נתיבים מקבילים מאפשרים לכם להציג בו-זמנית דף אחד או יותר באותה פריסה. זה שימושי במיוחד עבור פריסות מורכבות, כגון לוחות מחוונים (dashboards), שבהם חלקים שונים של הדף יכולים להיטען באופן עצמאי. נתיבים מקבילים מוגדרים באמצעות הסימן `@` ואחריו שם חריץ (slot) (למשל, `@sidebar`, `@main`).

דוגמה:

app/
  @sidebar/
    page.js  // תוכן עבור סרגל הצד
  @main/
    page.js  // תוכן עבור החלק הראשי
  default.js // חובה: מגדיר את פריסת ברירת המחדל עבור נתיבים מקבילים

קובץ ה-`default.js` נדרש בעת שימוש בנתיבים מקבילים. הוא מגדיר כיצד החריצים השונים משולבים ליצירת הפריסה הסופית.

// app/default.js
export default function RootLayout({ children: { sidebar, main } }) {
  return (
    <div style={{ display: 'flex' }}>
      <aside style={{ width: '200px', backgroundColor: '#f0f0f0' }}>
        {sidebar}
      </aside>
      <main style={{ flex: 1, padding: '20px' }}>
        {main}
      </main>
    </div>
  );
}

11. יירוט נתיבים (Intercepting Routes)

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

דוגמה:

app/
  (.)photos/
    [id]/
      page.js  // הנתיב המיורט
  feed/
    page.js  // הדף שבו מוצג מודאל התמונה

בדוגמה זו, כאשר משתמש לוחץ על תמונה בדף `/feed`, הנתיב `app/(.)photos/[id]/page.js` מיורט ומוצג כמודאל מעל דף ה-`/feed`. התחביר `(.)` מורה ל-Next.js לחפש רמה אחת למעלה (לספריית `app`) כדי למצוא את הנתיב `photos/[id]`.

שליפת נתונים עם ה-App Router

ה-App Router מספק תמיכה מובנית לשליפת נתונים באמצעות רכיבי שרת (Server Components) ורכיבי לקוח (Client Components). רכיבי שרת מרונדרים בשרת, בעוד שרכיבי לקוח מרונדרים בלקוח. זה מאפשר לכם לבחור את הגישה הטובה ביותר עבור כל רכיב בהתבסס על הדרישות שלו.

רכיבי שרת

רכיבי שרת הם ברירת המחדל ב-App Router. הם מאפשרים לכם לשלוף נתונים ישירות ברכיבים שלכם ללא צורך בנתיבי API נפרדים. זה יכול לשפר את הביצועים ולפשט את הקוד שלכם.

דוגמה:

// app/products/page.js
import React from 'react';

export default async function ProductsPage() {
  const products = await fetchProducts();

  return (
    <div>
      <h1>מוצרים</h1>
      <ul>
        {products.map((product) => (
          <li key={product.id}>{product.name}</li>
        ))}
      </ul>
    </div>
  );
}

async function fetchProducts() {
  // הדמיה של שליפת נתונים ממסד נתונים או API
  return new Promise((resolve) => {
    setTimeout(() => {
      const products = [
        { id: 1, name: 'מוצר א' },
        { id: 2, name: 'מוצר ב' },
        { id: 3, name: 'מוצר ג' },
      ];
      resolve(products);
    }, 500);
  });
}

בדוגמה זו, הפונקציה `fetchProducts` נקראת ישירות בתוך הרכיב `ProductsPage`. הרכיב מרונדר בשרת, והנתונים נשלפים לפני שה-HTML נשלח ללקוח.

רכיבי לקוח

רכיבי לקוח מרונדרים בלקוח ומאפשרים לכם להשתמש בתכונות צד-לקוח כמו מאזיני אירועים (event listeners), מצב (state), וממשקי API של הדפדפן. כדי להשתמש ברכיב לקוח, עליכם להוסיף את ההנחיה `'use client'` בראש הקובץ.

דוגמה:

// app/counter/page.js
'use client'

import React, { useState } from 'react';

export default function CounterPage() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>מונה</h1>
      <p>ספירה: {count}</p>
      <button onClick={() => setCount(count + 1)}>הגדל</button>
    </div>
  );
}

בדוגמה זו, הרכיב `CounterPage` הוא רכיב לקוח מכיוון שהוא משתמש ב-hook `useState`. ההנחיה `'use client'` מורה ל-Next.js לרנדר רכיב זה בצד הלקוח.

טכניקות ניתוב מתקדמות

ה-App Router מציע מספר טכניקות ניתוב מתקדמות שניתן להשתמש בהן ליצירת אפליקציות מורכבות ומתוחכמות.

1. מטפלי נתיבים (Route Handlers)

מטפלי נתיבים מאפשרים לכם ליצור נקודות קצה של API בתוך ספריית `app` שלכם. זה מבטל את הצורך בספריית `pages/api` נפרדת. מטפלי נתיבים מוגדרים בקבצים בשם `route.js` (או `route.ts`) ומייצאים פונקציות המטפלות בשיטות HTTP שונות (למשל, `GET`, `POST`, `PUT`, `DELETE`).

דוגמה:

// app/api/users/route.js
import { NextResponse } from 'next/server'

export async function GET(request) {
  // הדמיה של שליפת משתמשים ממסד נתונים
  const users = [
    { id: 1, name: 'ישראל ישראלי' },
    { id: 2, name: 'ישראלה ישראלי' },
  ];

  return NextResponse.json(users);
}

export async function POST(request) {
  const body = await request.json()
  console.log('נתונים שהתקבלו:', body)
  return NextResponse.json({ message: 'המשתמש נוצר' }, { status: 201 })
}

דוגמה זו מגדירה מטפל נתיב ב-`/api/users` המטפל בבקשות `GET` ו-`POST` כאחד. פונקציית `GET` מחזירה רשימת משתמשים, ופונקציית `POST` יוצרת משתמש חדש.

2. קבוצות נתיבים עם פריסות מרובות

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

דוגמה:

app/
  (marketing)/
    layout.js  // פריסה שיווקית
    about/
      page.js
    contact/
      page.js
  (admin)/
    layout.js  // פריסת ניהול
    dashboard/
      page.js

בדוגמה זו, דפי `about` ו-`contact` ישתמשו בפריסה `marketing`, בעוד שדף `dashboard` ישתמש בפריסה `admin`.

3. תווכה (Middleware)

תווכה (Middleware) מאפשרת לכם להריץ קוד לפני שבקשה מטופלת על ידי האפליקציה שלכם. זה שימושי למשימות כמו אימות, הרשאה, רישום (logging), והפניית משתמשים על בסיס מיקומם או המכשיר שלהם.

תווכה מוגדרת בקובץ בשם `middleware.js` (או `middleware.ts`) בשורש הפרויקט שלכם.

דוגמה:

// middleware.js
import { NextResponse } from 'next/server'

export function middleware(request) {
  // בדיקה אם המשתמש מאומת
  const isAuthenticated = false; // החלף בלוגיקת האימות שלך

  if (!isAuthenticated && request.nextUrl.pathname.startsWith('/admin')) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  return NextResponse.next();
}

// ראה "Matching Paths" למטה למידע נוסף
export const config = {
  matcher: '/admin/:path*',
}

דוגמה זו מגדירה תווכה הבודקת אם המשתמש מאומת לפני שהיא מאפשרת לו לגשת לכל נתיב תחת `/admin`. אם המשתמש אינו מאומת, הוא מופנה לדף `/login`.

שיטות עבודה מומלצות לניתוב מבוסס קבצים

כדי להפיק את המרב ממערכת הניתוב מבוססת הקבצים של ה-App Router, שקלו את השיטות המומלצות הבאות:

דוגמאות לבינאום (Internationalization) עם Next.js App Router

ה-Next.js App Router מפשט את תהליך הבינאום (i18n) באמצעות ניתוב מבוסס קבצים. הנה כיצד תוכלו ליישם i18n ביעילות:

1. ניתוב תת-נתיבים (Sub-path Routing)

ארגנו את הנתיבים שלכם לפי שפה (locale) באמצעות תת-נתיבים. לדוגמה:

app/
  [locale]/
    page.tsx         // דף הבית עבור השפה
    about/
      page.tsx     // דף אודות עבור השפה
// app/[locale]/page.tsx
import { getTranslations } from './dictionaries';

export default async function HomePage({ params: { locale } }) {
  const t = await getTranslations(locale);
  return (<h1>{t.home.title}</h1>);
}

// dictionaries.js
const dictionaries = {
  en: () => import('./dictionaries/en.json').then((module) => module.default),
  es: () => import('./dictionaries/es.json').then((module) => module.default),
};

export const getTranslations = async (locale) => {
  try {
    return dictionaries[locale]() ?? dictionaries.en();
  } catch (error) {
    console.error(`Failed to load translations for locale ${locale}`, error);
    return dictionaries.en();
  }
};

בהגדרה זו, מקטע הנתיב הדינמי `[locale]` מטפל בשפות שונות (למשל, `/en`, `/es`). התרגומים נטענים באופן דינמי בהתבסס על השפה.

2. ניתוב דומיינים (Domain Routing)

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

3. תווכה (Middleware) לזיהוי שפה

השתמשו בתווכה כדי לזהות אוטומטית את השפה המועדפת על המשתמש ולהפנות אותו בהתאם.

// middleware.js
import { NextResponse } from 'next/server';
import { match } from '@formatjs/intl-localematcher';
import Negotiator from 'negotiator';

let locales = ['en', 'es', 'fr'];

function getLocale(request) {
  const negotiatorHeaders = {};
  request.headers.forEach((value, key) => (negotiatorHeaders[key] = value));
  let languages = new Negotiator({ headers: negotiatorHeaders }).languages();

  try {
      return match(languages, locales, 'en'); // השתמש ב-"en" כשפת ברירת מחדל
  } catch (error) {
      console.error("Error matching locale:", error);
      return 'en'; // חזור לאנגלית אם ההתאמה נכשלת
  }
}

export function middleware(request) {
  const pathname = request.nextUrl.pathname;
  const pathnameIsMissingLocale = locales.every(
    (locale) => !pathname.startsWith(`/${locale}/`) && pathname !== `/${locale}`
  );

  if (pathnameIsMissingLocale) {
    const locale = getLocale(request);

    return NextResponse.redirect(
      new URL(
        `/${locale}${pathname.startsWith('/') ? '' : '/'}${pathname}`,
        request.url
      )
    );
  }
}

export const config = {
  matcher: [
    '/((?!api|_next/static|_next/image|favicon.ico).*)',
  ],
};

התווכה הזו בודקת אם לנתיב המבוקש יש קידומת שפה. אם לא, היא מזהה את השפה המועדפת על המשתמש באמצעות הכותרת `Accept-Language` ומפנה אותו לנתיב המתאים עם קידומת השפה. ספריות כמו `@formatjs/intl-localematcher` ו-`negotiator` משמשות לטיפול במשא ומתן על השפה.

Next.js App Router ונגישות גלובלית

יצירת יישומי אינטרנט נגישים גלובלית דורשת התייחסות מדוקדקת לעקרונות נגישות (a11y). ה-Next.js App Router מספק בסיס איתן לבניית חוויות נגישות, אך חיוני ליישם שיטות עבודה מומלצות כדי להבטיח שהיישום שלכם יהיה שמיש לכולם, ללא קשר ליכולותיהם.

שיקולי נגישות עיקריים

  1. HTML סמנטי: השתמשו באלמנטים סמנטיים של HTML (למשל, `<article>`, `<nav>`, `<aside>`, `<main>`) כדי לבנות את התוכן שלכם. זה מספק משמעות לטכנולוגיות מסייעות ועוזר למשתמשים לנווט באתר שלכם בקלות רבה יותר.
  2. תכונות ARIA: השתמשו בתכונות ARIA (Accessible Rich Internet Applications) כדי לשפר את הנגישות של רכיבים ווידג'טים מותאמים אישית. תכונות ARIA מספקות מידע נוסף על התפקיד, המצב והמאפיינים של אלמנטים לטכנולוגיות מסייעות.
  3. ניווט באמצעות מקלדת: ודאו שכל האלמנטים האינטראקטיביים נגישים באמצעות המקלדת. המשתמשים צריכים להיות מסוגלים לנווט ביישום שלכם באמצעות מקש `Tab` ולתקשר עם אלמנטים באמצעות מקש `Enter` או `Space`.
  4. ניגודיות צבעים: השתמשו בניגודיות צבעים מספקת בין טקסט לרקע כדי להבטיח קריאות למשתמשים עם לקויות ראייה. הנחיות הנגישות לתוכן אינטרנט (WCAG) ממליצות על יחס ניגודיות של לפחות 4.5:1 לטקסט רגיל ו-3:1 לטקסט גדול.
  5. טקסט חלופי לתמונות (Alt Text): ספקו טקסט חלופי תיאורי לכל התמונות. טקסט חלופי מספק אלטרנטיבה טקסטואלית לתמונות שניתן לקרוא על ידי קוראי מסך.
  6. תוויות טפסים: קשרו תוויות טפסים לשדות הקלט המתאימים להן באמצעות אלמנט `<label>`. זה מבהיר למשתמשים איזה מידע מצופה בכל שדה.
  7. בדיקה עם קורא מסך: בדקו את היישום שלכם עם קורא מסך כדי לוודא שהוא נגיש למשתמשים עם לקויות ראייה. קוראי מסך פופולריים כוללים NVDA, JAWS ו-VoiceOver.

יישום נגישות ב-Next.js App Router

  1. השתמשו ברכיב Link של Next.js: השתמשו ברכיב `<Link>` לניווט. הוא מספק תכונות נגישות מובנות, כגון טעינה מראש (prefetching) וניהול פוקוס.
  2. ניהול פוקוס: בעת ניווט בין דפים או פתיחת מודאלים, ודאו שהפוקוס מנוהל כראוי. הפוקוס צריך להיות מוגדר על האלמנט ההגיוני ביותר בדף או במודאל החדש.
  3. רכיבים מותאמים אישית נגישים: בעת יצירת רכיבים מותאמים אישית, ודאו שהם נגישים על ידי ביצוע העקרונות המתוארים לעיל. השתמשו ב-HTML סמנטי, תכונות ARIA וניווט מקלדת כדי להפוך את הרכיבים שלכם לשמישים לכולם.
  4. בדיקות סטטיות (Linting) ובדיקות אוטומטיות: השתמשו בכלי בדיקה סטטיים כמו ESLint עם תוספי נגישות כדי לזהות בעיות נגישות פוטנציאליות בקוד שלכם. כמו כן, השתמשו בכלי בדיקה אוטומטיים כדי לבדוק את היישום שלכם לאיתור הפרות נגישות.

סיכום

מערכת הניתוב מבוססת הקבצים של Next.js App Router מציעה דרך עוצמתית ואינטואיטיבית לבנות ולנווט ביישומים שלכם. על ידי הבנת מושגי הליבה והשיטות המומלצות המתוארות במדריך זה, תוכלו לבנות יישומי Next.js חזקים, ניתנים להרחבה וקלים לתחזוקה. התנסו בתכונות השונות של ה-App Router וגלו כיצד הוא יכול לפשט את זרימת העבודה שלכם בפיתוח ולשפר את חוויית המשתמש.