מבוא ל React Router גירסה 6

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

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

במקום זה הדרך המקובלת לעבור בין דפים נקראת Single Page Application. הרעיון שיש לנו רק קובץ HTML אחד עם סט אחד של קומפוננטות ריאקט, וקוד ריאקט יודע להציג את הקומפוננטה שמתאימה ל URL הנוכחי. ספריית react-router, עליה נלמד בפרק זה, מספקת דרך קלה לבניית יישומים כאלה בריאקט.

1. האתגרים ביישומי עמוד יחיד

לפני שנראה איך כותבים יישום Single Page, אולי אתם כבר שואלים את עצמכם בשביל מה צריך ספריה מיוחדת? למה זה כל כך קשה? אז הנה רשימת אתגרים שבלי React Router הייתם צריכים לפתור לבד:

  1. טעינת הדף הראשון ביישום עשויה להיות איטית, כי כל תוכן האתר (כל הקומפוננטות) מגיע במכה אחת.

  2. דפדפנים יודעים לטפל אוטומטית בניווט אחורה ושמירת היסטוריה בין דפים, אבל ביישומי עמוד יחיד עלינו לבנות מנגנונים כאלה לבד (או להשתמש בספריות קיימות) כדי לתת למשתמשים את אותה חוויה.

בנוסף ביישומי עמוד יחיד יש עוד מספר אתגרים שספריית react-router לא פותרת בשבילכם וכדאי להיות מודעים אליהם:

  1. התאמה למנועי חיפוש עשויה להיות מורכבת, כיוון שרק אחרי הפעלת קוד JavaScript אפשר לדעת מה להציג. לא כל מנועי החיפוש יודעים JavaScript ולכן לחלקם יהיה קשה לנווט באתר.

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

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

בואו נתקדם ל react-router ונראה איך היא עוזרת לנו לכתוב יישומי Single Page בקלות.

2. איך עובד React Router

ב React Router אנחנו מגדירים טבלת ניתוב, שזו טבלה שאומרת ל react-router עבור כל נתיב איזה קומפוננטה צריך להציג. את הטבלה אנחנו כותבים בתור קומפוננטות ריאקט, ו react-router "מחליף" את כל בלוק הקומפוננטות הזה בקומפוננטה האמיתית שצריך להראות עבור הנתיב הנוכחי. אפשר לדמיין את ההתנהגות שלו בתור switch/case מתוחכם, שמסתכל על שורת הכתובת ועל טבלת הניתוב, ולפי מה שכתוב שם מחליט איזה קומפוננטה להראות.

כך נראית טבלת ניתוב פשוטה:

<BrowserRouter>
  <Routes>
    <Route path="/" element={<Home />} />
    <Route path="/about" element={<About />} />
    <Route path="/contact" element={<Contact />} />
  </Routes>
</BrowserRouter>

ננסה לקרוא את זה יחד-

  1. אם הכתובת בדפדפן היא / אז תחליף את כל הבלוק שבתוך Routes בקומפוננטה Home.

  2. אם הכתובת בדפדפן היא /about אז תחליף את כל הבלוק שבתוך Routes בקומפוננטה About.

  3. אם הכתובת בדפדפן היא /contact אז תחליף את כל הבלוק שבתוך Routes בקומפוננטה Contact.

את קומפוננטת BrowserRouter אני צריך לשים פעם אחת ביישום מסביב לכל הקומפוננטות שלי, אבל את Routes אני יכול לשים בכמה מקומות, וכל מקום בו אני שם Routes יוחלף בקומפוננטה שמתאימה לנתיב.

הנה קובץ App.js שממחיש את המשחק:

import './App.css';
import {
  BrowserRouter,
  Routes,
  Route,
} from "react-router-dom";

function App() {
  return (
    <>
      <h1>Welcome To React Router</h1>
      <BrowserRouter>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
          <Route path="/contact" element={<Contact />} />
        </Routes>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
          <Route path="/contact" element={<Contact />} />
        </Routes>
      </BrowserRouter>
    </>
  );
}

function Home() {
  return (<p>Home Page</p>);
}

function About() {
  return (<p>About Page</p>);
}

function Contact() {
  return (<p>Contact Page</p>);
}

export default App;

נסו להפעיל את היישום ולהיכנס לכתובות:

http://localhost:3000/ http://localhost:3000/about http://localhost:3000/contact

תוכלו לשים לב שכל כניסה לדף מחליף את קומפוננטת Routes בקומפוננטה שמתאימה לכתובת, ולכן אני מקבל תמיד את שורת הכותרת ומתחתיה פעמיים את הקומפוננטה הראשית של הדף.

3. מעבר בין דפים

כשאנחנו עוברים בין דפים באמצעות שינוי שורת הכתובת בדפדפן או באמצעות אלמנט a, הדפדפן פונה מחדש לשרת וטוען את העמוד שמתאים לכתובת החדשה. בגלל שהשרת הוא שרת פיתוח של create-react-app, השרת הזה יודע להגיש תמיד את אותו קובץ HTML, ולכן אנחנו מקבלים את אותה אפליקציה בעמוד שמתאים לכתובת החדשה. אבל זה מנגנון בזבזני - הרי יש לי כבר בדפדפן את כל הקומפוננטות וכל המידע שצריך בשביל להציג את העמוד הבא, בשביל מה לי ללכת שוב לשרת רק כדי לקבל את אותם קבצי js, css ו html שכבר יש לי?

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

נעדכן את העמוד שלנו עם שתי שורות ניווט כדי להמחיש את ההתנהגות של Link:

function App() {
  return (
    <BrowserRouter>
      <h1>Welcome To React Router</h1>
      <nav>
        <h2>Navigation with &lt;Link/&gt; Component</h2>
        <Link to="/">Home Page</Link>
        <Link to="/about">About Page</Link>
        <Link to="/contact">Contact Page</Link>
      </nav>
      <nav>
        <h2>Navigation with &lt;a/&gt; Tag</h2>
        <a href="/">Home Page</a>
        <a href="/about">About Page</a>
        <a href="/contact">Contact Page</a>
      </nav>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/contact" element={<Contact />} />
      </Routes>
    </BrowserRouter>
  );
}

שימו לב איך כשאני עובר בין הדפים עם ה Link המעבר הוא חלק, אבל כשאני עובר באמצעות לחיצה על ה a אני רואה בדפדפן טעינה מחדש של העמוד.

4. איך ריאקט ראוטר פותרת חלק מהבעיות שלנו

ריאקט ראוטר לא פותר את כל האתגרים שדיברנו עליהם בתחילת השיעור, אבל הוא כן עוזר עם חלקם:

  1. ריאקט ראוטר מטפל בשבילנו בכל נושא ההיסטוריה ואפשרות החזרה אחורה מהדפדפן מאחר וכל דף פנימי מקבל URL משלו.

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

  3. ריאקט ראוטר תומך ב Server Side Rendering. פיצ'ר זה הוא מעבר להיקף של קורס זה מאחר והוא מערב שינוי קוד בצד השרת. בגדול מה ש SSR אומר זה שאנחנו מייצרים את כל ה HTML בשרת לפני ששולחים אותו ללקוח (בעצם מריצים ריאקט בצד השרת ולא בדפדפן). שילוב SSR יאפשר לייצר HTML פשוט ללקוחות שצריכים אותו כדוגמת קוראי מסך ומנועי חיפוש.

5. בונוס: איך לחבר אפליקציית React Router לשרת Node.JS אמיתי

בעבודה עם שרת אמיתי בדרך כלל נצטרך להגדיר לכל נתיב איזה דף HTML להגיש באותו נתיב. אם השרת שלכם כתוב ב Node.JS אולי אתם משתמשים במידלוור כדי להגיש קבצים סטטיים מתיקיית public לפי שם הקובץ שמתאים לנתיב אליו ניסו לגלוש.

הבעיה שכשמחברים את זה ל react-router זה לא עובד כל כך טוב - ריאקט ראוטר דורש שבכניסה לכל נתיב באפליקציה נקבל תמיד את אותו קובץ html ואת אותם קבצי js ו css, והוא כבר בצד לקוח ידאג להראות את הקומפוננטה המתאימה.

בשרת Node.JS אנחנו יכולים לקבל את ההתנהגות הזאת באמצעות הגדרת נתיב כזה:

app.get('/*', function(req, res) {
  res.sendFile(path.join(__dirname, 'path/to/your/index.html'), function(err) {
    if (err) {
      res.status(500).send(err)
    }
  })
})