מדריך Vue למתחילים - חלק 6 - יישומי Single Page Apps עם Vue Router

ויו היא ספריה לפיתוח יישומי צד-לקוח מורכבים. בשבועות האחרונים אני מפרסם לאט מדריך Vue למתכנתי Front End שרוצים להתחיל לעבוד עם הספריה. עד כה פורסמו הפרקים:

  1. פרק 1 - פיתוח קומפוננטה ראשונה

  2. פרק 2 - תקשורת בין קומפוננטות

  3. פרק 3 - תבניות דינאמיות

  4. פרק 4 - ממשק ההרכבה Composition API

  5. פרק 5 - דוגמת פיתוח נגן YouTube ב Vue

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

1. מהו Single Page App

אחת המהפכות המעניינות בפיתוח ווב היה המעבר מפיתוח "אתרים" לפיתוח "אפליקציות". התהליך הזה הושפע ממספר פיתוחים מקבילים, מצד אחד שיפורים ביכולות של דפדפנים איפשרו לכתוב יישומים עם קוד JavaScript מאוד מורכב, ומצד שני ההשראה שקיבלו מפתחי ווב מעולם ה Mobile ומהפופולריות של חווית המשתמש בתוך אפליקציות בטלפונים.

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

בתוך דפדפן הקונספט של שינוי רק חלקים מהעמוד היה בשימוש עוד מאזור 2005 וזכה לשם Ajax. אבל רק מ 2010, במקביל לפופולריות של אפליקציות במובייל, מתכנתים החלו לחשוב ברצינות על שינוי העמוד כולו ב Ajax כדי לתת חווית משתמש מיידית במעברים בין דפים. שינוי זה מביא איתו גם הזדמנויות וגם אתגרים: בצד ההזדמנויות כשאני מחליף רק את תוכן הדף ב Ajax אני צריך לטעון הרבה פחות מידע מהשרת (לא צריך לטעון את כל ה HTML ואת כל הקבצים שסביבו); אבל בצד האתגרים צריך לזכור שדפדפנים לא נועדו לשיטת עבודה זו, ולכן קוד JavaScript צריך למלא תפקידים שבאופן רגיל עושה דפדפן כדוגמת אפשרות לדפדוף אחורה בדפים, אפשרות לשמירת דף מסוים מהאתר בתור סימניה, שמירה על מיקום הגלילה בטעינה מחדש של עמוד אבל גלילה לראש העמוד במעבר בין עמודים ומשימות נוספות בסגנון זה.

באזור שנת 2015 פרדיגמת פיתוח Web Applications החלה לצבור פופולריות אבל אז עוד היה קשה לבנות אפליקציות כאלה כי רוב כלי הפיתוח והספריות שהיו בשימוש נועדו לבניית אתרים - בהם קובץ HTML מייצג דף באתר וגלישה לדף אחר מחייבת מעבר לקובץ HTML אחר. כשפריימוורקס פיתוח ווב החלו להופיע אחת ההבטחות שלהן היתה להפוך את תהליך פיתוח ה"אפליקציה" להרבה יותר קל ולדאוג בשבילנו לכל משימות הניווט במקום הדפדפן. בעולם של היום הרבה פעמים כבר יותר קל לכתוב Web Application מאשר "אתר" קלאסי, בגלל שכלי הפיתוח מותאמים לגישה זו.

המאפיינים המרכזיים של Single Page Application הם:

  1. דף HTML יחיד שלא מכיל תוכן, וקוד JavaScript שאחראי על מילוי התוכן עצמו.

  2. ה URL משתנה בהתאמה לתוכן המשתנה על המסך, למרות שאין טעינה של דף HTML חדש.

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

  4. בדרך כלל עבודה בתוך Framework שדואג למלא את התפקיד של הדפדפן כשעברנו לגישה זו.

ב Vue הספריה שעוזרת לנו לבנות Single Page Applications נקראת Vue Router. במדריך זה נראה איך להתקין אותה ואיך לבנות באמצעותה יישומי עמוד יחיד.

2. ההבדל בין Hash Navigation ל History Navigation

ביישומי עמוד יחיד ה URL משתנה יחד עם התוכן שמשתנה על המסך, אבל תקופה ארוכה ובזמן שמתכנתים כבר כתבו יישומי Single Page דפדפנים עדיין לא איפשרו לנו לשנות את ה URL בלי לטעון דף חדש. בשביל להתמודד עם זה השתמשנו ב"מעקף" שנקרא Hash.

ב HTML תגית a היא קיצור של המילה anchor או עוגן. כמו שהיא מייצגת קישור היא גם יכולה לייצג את היעד שאליו מקשרים, או את העוגן על העמוד שאליו אנחנו רוצים ללכת. אם תכתבו בכל מקום בדף HTML את התוכן:

<a name="tasks">Tasks</a>

אז תוכלו לייצר קישור עם סימן # שיוביל לדף ה HTML הזה ולנקודה הספציפית tasks בתוך הדף. אם לדוגמה כתבתם את תגית ה a בתוך דף HTML שנקרא demo.html אז הקישור demo.html#tasks יוביל את הגולש ישירות למקום בעמוד בו מופיע ה a, כלומר ישירות לרשימת המשימות.

שינוי ה Hash היה הדרך היחידה שהיתה לנו לשנות את ה URL בלי לחייב גישה מחדש לשרת, ולכן קונספט ראשון של Single Page Application שנוצר נקרא Hash Navigation. בסכימה זו כל "דף" ביישום הוא פשוט Hash שונה בתוך HTML יחיד. כך יהיו לנו index.html#home, index.html#about, index.html#contact ואפילו index.html#products/2.

בכל אחד מהמקרים הדפדפן יפנה לשרת כדי למשוך את הקובץ index.html, וקוד JavaScript יסתכל על ה Hash וימלא את התוכן שאנחנו רואים בעמוד כדי להתאים ל"דף" בו רצינו לבקר.

לימים דפדפנים הוסיפו תמיכה במנגנון שנקרא History API. במנגנון זה אפשר היה כבר מתוך קוד JavaScript לשנות את ה URL בלי שנצטרך פניה לשרת, ולכן אני יכול להשתמש ב URL-ים רגילים לגמרי, וכשמשתמש רוצה לעבור דף אני פשוט מתוך JavaScript בצורה יזומה משנה את ה URL. עכשיו נוצרה בעיה חדשה שהיא צד השרת - שהיה צריך להתמודד עם כניסה לכל מיני URL-ים אפילו שאין קבצי HTML או קוד צד שרת שיודע לטפל בהם. לדוגמה יכול להיות שיש לי רק קובץ index.html על השרת ואז בטעינה הראשונה ה URL הוא index.html, אבל משתמש עובר לדף "אודות" וקוד JavaScript משנה את ה URL ל about.html (ואין קובץ כזה על השרת). עכשיו עד לפה הכל עובד כי שינוי ה URL בוצע רק בקוד JavaScript בלי פניה לשרת, אבל אם משתמש ישמור את ה URL החדש בתור סימניה או ירענן את העמוד, השרת יצטרך להתמודד עם פניה לנתיב about.html שלא קיים שם.

כשאנחנו כותבים יישומי Single Page היום יש לנו לכן שתי אפשרויות:

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

  2. אנחנו יכולים לעדכן את קוד צד השרת כך שיענה לכל הבקשות (גם כאלה לדפים שהוא לא מכיר) ותמיד יחזיר את קובץ ה HTML הראשי של האפליקציה.

בדוגמאות בפוסט זה בשביל לא להתעסק עם שינויים בקוד צד שרת אני אשתמש ב Hash History. אתם תראו שב Vue מאוד קל לבחור בין השניים, וכשתרגישו מוכנים לשנות את קוד צד השרת כך שגם יתאים למבנה של Single Page App תוכלו להחליף ל History API החדש יותר.

3. יצירת SPA ראשון עם Vue Router

אחרי כל מה שדיברנו על Single Page Applications הגיע הזמן לראות דוגמה לאחד כזה באמצעות Vue ו Vue Router. התחילו ביצירת פרויקט Vue והוסיפו לו את vue-router עם הפקודה:

yarn add vue-router@4

שימו לב שנכון לכתיבת הפוסט ברירת המחדל היא vue-router בגירסה 3 הישנה יותר ולכן אני משתמש ב @ כדי לקחת את הגירסה החדשה.

בשביל לכתוב יישום Single Page עם Vue Router אני בסך הכל צריך לטפל במספר נקודות:

  1. אני צריך להגדיר איזה נתיבים האפליקציה שלי מכירה

  2. אני צריך לאתחל את אפליקציית ה Vue שלי ולהוסיף אליה את Vue Router עם רשימת הנתיבים

  3. אני צריך להשתמש בקומפוננטה מיוחדת של vue-router שנקראת router-link כדי לנווט בין דפים

  4. אני צריך להשתמש בקומפוננטה מיוחדת של vue-router שנקראת router-view כדי להציג תוכן של דף שמשתנה כשמשתמשים עוברים בין URL-ים.

בואו נראה את ארבעת הנקודות בקוד. תחילה בקובץ main.js נכתוב את הקוד הבא:

import { createApp } from 'vue';
import * as VueRouter from 'vue-router';

import Home from './components/Home';
import About from './components/About';
import Contact from './components/Contact';
import App from './App.vue';

const routes = [
  { path: '/', component: Home },
  { path: '/about', component: About },
  { path: '/contact', component: Contact },
];

const router = VueRouter.createRouter({
  history: VueRouter.createWebHashHistory(),
  routes,
});

createApp(App)
  .use(router)
  .mount('#app')

לאחר מכן נמשיך ליצירת הקומפוננטות: בקוד אני מתיחס ל-3 קומפוננטות בשם Home, About ו Contact. כל קומפוננטה לא צריכה לכלול יותר מדי קוד וזו דוגמה לקומפוננטת Home אצלי:

<template>
  <p>This is my homepage</p>
</template>

<script>
export default {
  name: "Home",
};
</script>

ובסיום נעדכן את הקובץ App.vue לתוכן הבא:

<template>
  <div class="container">
    <nav>
      <router-link to="/">Home</router-link>
      <router-link to="/about">About Us</router-link>
      <router-link to="/contact">Contact Us</router-link>
    </nav>
    <main>
      <router-view />
    </main>
  </div>
</template>

<script>
export default {
  name: 'App',
}
</script>

<style>

html, body {
  padding: 0; margin: 0;
}

nav {
  padding: 1rem;
  padding-left: 0.1rem;
  background: #595959;
}

nav a {
  margin: 0.2rem;
  text-decoration: none;
  background: #99aabc;
  padding: 0.2rem 0.5rem;
  width: 5rem;
  text-align: center;
  display: inline-block;
  color: #2211f1;
}

nav a::visited {
  color: #2211f1;
}

nav a.router-link-active {
  background: yellow;
}

</style>

את התוצאה אפשר לראות בקודסנדבוקס הבא:

ובקישור: https://codesandbox.io/s/vue-router-demo-1-cp0e7?file=/src/App.vue.

הנקודה הראשונה היתה הגדרת הנתיבים. שימו לב לקטע הבא מהקובץ main.js:

const routes = [
  { path: '/', component: Home },
  { path: '/about', component: About },
  { path: '/contact', component: Contact },
];

הקטע מגדיר מערך של 4 אוביקטים, כל אוביקט מייצג "נתיב". לנתיב יש את ה URL שלו ואת הקומפוננטה שתופיע כשמשתמש ייכנס לנתיב.

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

const router = VueRouter.createRouter({
  history: VueRouter.createWebHashHistory(),
  routes,
});

createApp(App)
  .use(router)
  .mount('#app')

הפונקציה createRouter יוצרת את אוביקט ה Router, והפונקציה use מוסיפה את ה Router ליישום. הפקודה createWebHashHistory היא זאת שבגללה כל הנתיבים מופיעים עם סימן ה #. האלטרנטיבה נקראת createWebHistory ותשתמש ב History API.

בתוך הקובץ App.vue אפשר לראות שהגדרתי בתבנית את כפתורי הניווט בתור router-link:

    <nav>
      <router-link to="/">Home</router-link>
      <router-link to="/about">About Us</router-link>
      <router-link to="/contact">Contact Us</router-link>
    </nav>

נכון, במקום herf אנחנו משתמשים במאפיין to, אבל חוץ מזה זה מספיק דומה ל a. בונוס נחמד שקיבלנו הוא CSS Class בשם המיוחד router-link-active שיתווסף אוטומטית ל router-link שמייצג את הדף שכרגע אני נמצא בו. קוד ה CSS הבא גורם לקישור זה לקבל רקע צהוב:

nav a.router-link-active {
  background: yellow;
}

שורה אחרונה בטמפלייט באותו קובץ App.vue היא:

<main>
    <router-view />
</main>

לתוך אלמנט זה Vue כותב את הקומפוננטה המתאימה לנתיב מבין הקומפוננטות שמופיעות בטבלת הנתיבים שלו.

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

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

  1. אתר של חנות שמציג מוצרים שונים - התבנית של דף המוצר זהה לכל המוצרים, אבל התוכן שונה בין מוצר למוצר.

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

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

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

במצב עניינים רגיל פרמטר שקומפוננטה מקבלת עובר אליה בתור Prop; אבל כשהקומפוננטה נמצאת בתור נתיב ב Router הפרמטר עובר בצורה קצת שונה. בשביל לראות את זה נוסיף לאתר שלנו דף של חנות מרצ'נדייז שמוכרת בובות של מלחמת הכוכבים. כל "מוצר" יהיה בסך הכל דף עם שם של דמות אחרת מהסרט. לפני המעבר לפרודקשן נצטרך להוסיף עיצוב, מחיר ותיאור לכל בובה - אבל בינתיים ובשביל הדוגמה שם הדמות יספיק. אם נדמיין את מבנה הנתיבים אפשר לדמיין נתיב ראשי של החנות /products, ואחריו נתיבים לכל אחד מהמוצרים כמו /products/1, /products/2 ו /products/7. כל מספר מייצג מזהה של מוצר אחר.

אנחנו כבר יודעים שהנתיב הראשי יהיה המחרוזת /products. לגבי נתיבי המוצרים הם נקראים נתיבים עם פרמטרים ואנחנו מתארים אותם באמצעות החלפת החלק המשתנה (המספר) במילה שתהיה שם הפרמטר עם תחילית :, כלומר במקרה של החנות יהיה לנו /products/:id.

בנוסף שני הנתיבים /products ו /products/:id כוללים חלקים משותפים: בנתיב products אני מצפה לראות רשימה של כל המוצרים באיזשהו תפריט צד ולהשאיר את האזור המרכזי ריק כי כרגע אנחנו לא מסתכלים על אף מוצר; ובנתיב /products/:id, לא משנה עבור איזה id, אני מצפה לראות את אותו תפריט צד עם רשימת המוצרים אבל הפעם בחלק המרכזי את פרטי המוצר שמתאים ל id.

יש מספר דרכים להציג תוכן משותף בין מספר דפים ב Vue, אבל בגלל שאנחנו מדברים כאן על Vue Router אני אראה את הדרך שמשתמשת ב Router - והיא השימוש ב children. הרעיון הוא שכל קומפוננטה יכולה להכיל תתי-נתיבים עבור מופעים של router-view שנמצאים בתוכה. כלומר אני יכול לכתוב בתוך הגדרת נתיב:

{
    path: '/products',
    component: Store,
    children: [
        { path: ':id', component: ProductPage },
    ]
}

ובתוך הקומפוננטה Store לייצר router-view, ואז אם הנתיב מתאים ל /products אותו router-view פנימי יופיע ריק; ואם הנתיב מתאים ל /products/:id (למשל אם זה /products/8) אז בתוך ה router-view תופיע הקומפוננטה ProductPage שתקבל את הפרמטר id.

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

או בקישור: https://codesandbox.io/s/vue-router-demo-1-forked-e2nq9?file=/src/assets/main.css.

הקוד כולל קצת ארגון מחדש בהשוואה לדוגמה הקודמת. הוצאת את הנתיבים לקובץ routes.js כדי שלא יתערבבו עם דברים אחרים בקובץ main.js. לקובץ routes הוספתי את הנתיב products ואת הילדים שלו בנתיב /products/:id. ככה זה נראה:

import Home from "./components/Home";
import About from "./components/About";
import Contact from "./components/Contact";
import Store from "./components/Store";
import ProductPage from "./components/ProductPage";

const routes = [
  { path: "/", component: Home, name: "home" },
  { path: "/about", component: About, name: "about" },
  { path: "/contact", component: Contact, name: "contact" },
  {
    path: "/products",
    component: Store,
    name: "shop",
    children: [
      { path: "/products/:id", component: ProductPage, name: "product" }
    ]
  }
];

export default routes;

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

<template>
  <div class='buy-page'>
    <h1>Buy Some Stuff</h1>
    <p>Please find below the descriptions for our star wars action figures</p>

    <div class="container">
      <nav class="sidenav">
        <ul>
          <li v-for="n in 10" :key="`item-${n}`"><router-link :to="`/products/${n}`">Product {{n}}</router-link></li>
        </ul>
      </nav>
      <router-view />
    </div>
  </div>
</template>

<script>
  export default {
  }
</script>

<style scoped>
.buy-page {
  padding: 1rem;
}

.container {
  display: flex;
}

nav { 
  margin-right: 2rem;
}

ul {
  list-style: none;
  padding: 0;
}

</style>

הקובץ המלהיב באמת הוא ProductPage.vue:

<template>
  <div v-if="data">
    <p>Product page for {{ data.name }}</p>
  </div>
  <div v-else>
    Loading...
  </div>
</template>

<script>
import { useSWR } from 'vswr'
import { useRoute } from 'vue-router'

export default {
  name: 'ProductPage',
  setup() {
    const route = useRoute()
    const { data, error } = useSWR(() => `https://swapi.dev/api/people/${route.params.id}`);

    return {
      data,
      error,
    }
  }
}
</script>

<style scoped>
</style>

קוד לא ארוך אבל עושה מספר דברים מעניינים. תחילה בתוך פונקציית setup הפונקציה useRoute מחזירה את "הנתיב" הנוכחי. דרך אוביקט הנתיב אני יכול לקבל את הפרמטר id באמצעות הקריאה route.params.id.

שנית הפונקציה useSWR עליה כבר דיברתי באחד הפרקים הקודמים במדריך זה, מקבלת פונקציה שמחזירה URL ומושכת את המידע מאותו URL. ברגע שיהיה שינוי ב route.params.id הפונקציה תופעל שוב ותמשוך את המידע החדש.

במבנה כזה אנחנו מושכים את המידע מהשרת אחרי שהקומפוננטה ProductPage נכנסה לעמוד ולכן בתבנית של הקומפוננטה אני מטפל גם במקרה בו המידע עדיין לא זמין. זה ה if/else שמופיע שם שמציג את המילה Loading אם data עדיין ריק.

השינוי האחרון הוא התוספת:

<router-link to="/products">Shop</router-link>

בתפריט העליון בקומפוננטה App.vue.

נ.ב. אתם יכולים לשים לב שהוספתי גם קובץ CSS בשם assets/main.css. בעבודה עם vue אני מעדיף להוסיף קובץ CSS מסודר עבור כללי CSS גלובאליים שמשפיעים על כל הדפים, ולהשתמש בהגדרות ה style בתוך קבצי vue בשביל כללי עיצוב שרלוונטים לקומפוננטה מסוימת.

5. נתיבים עם שמות

טריק אחרון של Vue Router שאני רוצה להראות הוא התמיכה בניווט לפי שמות. שימו לב לפקודות ה router-link בטמפלייט שלנו:

<router-link to="/contact">Contact Us</router-link>

אם מחר אני ארצה לשנות את מבנה הנתיב, למשל לשנות את ה URL שיהיה /contact-us אני אצטרך ללכת לכל router-link שמוביל לנתיב זה ולשנות אותו, ובמערכת גדולה זה עלול להיות ארוך ומועד לטעויות. טריק נחמד שאפשר לעשות במקום הוא להוסיף מאפיין name בהגדרת הנתיב באופן הבא:

{ path: "/contact", component: Contact, name: "contact" },

ואז ב router-link אני יכול להשתמש באותו name שהוא מזהה פנימי שלא קשור ל URL. קוד ה router-link שישתמש ב name יהיה:

<router-link :to="{ name: 'contact' }">Contact Us</router-link>

בנתיב עם פרמטרים, לדוגמה בקישור לדף מוצר, הקוד יראה כך:

<router-link :to="{ name: 'product', params: { id: 7 } }">Product 7</router-link>

מבנה כזה מאפשר לי לשנות את ה URL-ים בלי שאצטרך לרוץ לשנות בהתאמה את כל הלינקים בכל המערכת.

6. עכשיו אתם

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

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

  2. אחרי שיש לכם משחק זיכרון נמשיך לשילוב ה Router. כתבו מסך פתיחה הכולל תיבה לבחירת שם משתמש וכפתור "התחילו משחק". לחיצה על הכפתור אחרי בחירת שם משתמש מתחילה משחק.

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

  4. הוסיפו למסך הפתיחה קישור לטבלת השיאים שמראה את 5 השחקנים שסיימו את המשחק הכי מהר. מטבלת השיאים אפשר לחזור למסך הפתיחה אבל לא להתחיל משחק חדש.

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

  1. שימוש ב Programmatic Navigation כדי לנווט בצורה יזומה כשמשתמש לוחץ על הכפתור להמשך.

  2. שימוש ב Navigation Guards כדי למנוע ממשתמש להגיע לדף המשחק אם שם המשתמש הוא ריק.

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