Kurs Vue.js · Moduł 10: Vue Router

Dynamiczne Trasy

2 min czytania
W tej lekcji6

Każdy modul bazy NOVA LAB ma swój unikalny identyfikator. Podobnie możemy tworzyć dynamiczne ścieżki w aplikacji - trasy, które przyjmują parametry w URL.

Route Params

Parametr dynamiczny definiujemy przez dwukropek : w sciezce:

1const routes = [
2  {
3    path: '/system/:id',
4    name: 'system-detail',
5    component: SystemDetail
6  },
7  {
8    path: '/crew/:name',
9    name: 'crew-detail',
10    component: CrewDetail
11  },
12  // Wiele parametrow
13  {
14    path: '/modułe/:moduleId/system/:systemId',
15    name: 'modułe-system',
16    component: ModuleSystem
17  }
18]

Odczyt Parametrow - useRoute()

Composable useRoute() daje dostęp do informacji o aktualnym route, włącznie z parametrami:

1<script setup>
2import { useRoute } from 'vue-router'
3
4const route = useRoute()
5
6// Dostęp do parametrow
7console.log(route.params.id) // np. '42'
8console.log(route.path)      // np. '/system/42'
9console.log(route.name)      // np. 'system-detail'
10</script>
11
12<template>
13  <div>
14    <h1>System #{{ route.params.id }}</h1>
15  </div>
16</template>

Nawigacja z Parametrami

1<template>
2  <div v-for="system in systems" :key="system.id">
3    <!-- String URL -->
4    <RouterLink :to="`/system/${system.id}`">
5      {{ system.name }}
6    </RouterLink>
7
8    <!-- Object notation (zalecana) -->
9    <RouterLink :to="{ name: 'system-detail', params: { id: system.id } }">
10      {{ system.name }}
11    </RouterLink>
12  </div>
13</template>

Reaktywnosc na Zmiany Params

Gdy zmieniasz parametr w ramach tego samego route, komponent NIE jest odmontowywany. Używaj watch do reagowania na zmiany:

1<script setup>
2import { ref, watch } from 'vue'
3import { useRoute } from 'vue-router'
4
5const route = useRoute()
6const systemData = ref(null)
7
8watch(
9  () => route.params.id,
10  (newId) => {
11    // Załaduj dane nowego systemu
12    loadSystem(newId)
13  },
14  { immediate: true }
15)
16</script>

Query Parameters

Parametry zapytania (query) to czesc URL po znaku ?:

1<script setup>
2import { useRoute, useRouter } from 'vue-router'
3
4const route = useRoute()
5const router = useRouter()
6
7// URL: /systems?modułe=habitat&sort=priority
8console.log(route.query.modułe) // 'habitat'
9console.log(route.query.sort)   // 'priority'
10
11// Nawigacja z query
12function filterByModule(modułe) {
13  router.push({
14    name: 'systems',
15    query: { modułe, sort: 'priority' }
16  })
17}
18</script>

Opcjonalne i Walidowane Parametry

1const routes = [
2  // Opcjonalny parametr z ?
3  { path: '/system/:id?' },
4
5  // Regex - tylko liczby
6  { path: '/system/:id(\\d+)' },
7
8  // Powtarzalny parametr
9  { path: '/modules/:ids+' }
10]
Kod do tej lekcji: App.vue
1<script setup>
2import { ref } from 'vue'
3
4// Symulacja nested routes
5const currentPath = ref('/mission-control')
6const sidebarItems = [
7  { path: '/mission-control', label: 'Przeglad', name: 'overview' },
8  { path: '/mission-control/systems', label: 'Systemy', name: 'systems' },
9  { path: '/mission-control/crew', label: 'Zaloga', name: 'crew' }
10]
11
12function navigate(path) {
13  currentPath.value = path
14}
15
16const isActive = (path) => currentPath.value === path
17</script>
18
19<template>
20  <div class="nova-lab">
21    <h1>NOVA LAB - Nested Routes</h1>
22
23    <div class="layout">
24      <aside class="sidebar">
25        <h3>Centrum Kontroli Misji</h3>
26        <nav>
27          <a
28            v-for="item in sidebarItems"
29            :key="item.path"
30            @click.prevent="navigate(item.path)"
31            :class="{ active: isActive(item.path) }"
32            href="#"
33          >
34            {{ item.label }}
35          </a>
36        </nav>
37
38        <div class="config-info">
39          <pre>
40children: [
41  { path: '', component: Overview },
42  { path: 'systems', component: Systems },
43  { path: 'crew', component: Crew }
44]</pre>
45        </div>
46      </aside>
47
48      <main class="content">
49        <div v-if="currentPath === '/mission-control'" class="child-page">
50          <h2>Przeglad Misji</h2>
51          <p>Domyslny widok (path: '')</p>
52          <div class="stats">
53            <div class="stat">
54              <span class="label">Status misji</span>
55              <span class="value ok">AKTYWNA</span>
56            </div>
57            <div class="stat">
58              <span class="label">Dzien misji</span>
59              <span class="value">247</span>
60            </div>
61            <div class="stat">
62              <span class="label">Zaloga</span>
63              <span class="value">12 osob</span>
64            </div>
65          </div>
66        </div>
67
68        <div v-else-if="currentPath === '/mission-control/systems'" class="child-page">
69          <h2>Systemy Bazy</h2>
70          <p>Child route: path: 'systems'</p>
71          <ul class="systems-list">
72            <li>Oxygen Generator - ONLINE</li>
73            <li>Water Recycler - ONLINE</li>
74            <li>Power Grid - WARNING</li>
75            <li>Communications - ONLINE</li>
76          </ul>
77        </div>
78
79        <div v-else-if="currentPath === '/mission-control/crew'" class="child-page">
80          <h2>Zaloga</h2>
81          <p>Child route: path: 'crew'</p>
82          <ul class="crew-list">
83            <li>Dr. Nova - Commander</li>
84            <li>Eng. Kowalski - Systems</li>
85            <li>Dr. Chen - Science</li>
86          </ul>
87        </div>
88      </main>
89    </div>
90  </div>
91</template>
92
93<style scoped>
94.nova-lab {
95  background: #0a0e27;
96  color: #00ff88;
97  min-height: 100vh;
98  padding: 20px;
99  font-family: monospace;
100}
101h1 { color: #00ffff; margin-bottom: 15px; }
102h2 { color: #00b4d8; }
103h3 { color: #00ffff; margin-bottom: 10px; }
104p { color: #c0c0c0; }
105.layout {
106  display: grid;
107  grid-template-columns: 220px 1fr;
108  gap: 15px;
109}
110.sidebar {
111  background: rgba(0, 180, 216, 0.05);
112  border: 1px solid #1a1e37;
113  padding: 15px;
114}
115.sidebar nav { display: flex; flex-direction: column; gap: 5px; }
116.sidebar a {
117  color: #00ff88;
118  text-decoration: none;
119  padding: 8px;
120  border-left: 2px solid transparent;
121  cursor: pointer;
122}
123.sidebar a.active {
124  border-left-color: #00ffff;
125  color: #00ffff;
126  background: rgba(0, 255, 255, 0.05);
127}
128.config-info { margin-top: 15px; }
129.config-info pre {
130  font-size: 10px;
131  color: #888;
132  background: #1a1e37;
133  padding: 8px;
134}
135.content {
136  background: rgba(0, 180, 216, 0.05);
137  border: 1px solid #1a1e37;
138  padding: 15px;
139}
140.child-page { min-height: 200px; }
141.stats { display: flex; gap: 15px; margin-top: 15px; flex-wrap: wrap; }
142.stat {
143  background: #1a1e37;
144  padding: 10px 15px;
145  border: 1px solid #00b4d8;
146}
147.stat .label { display: block; color: #888; font-size: 11px; }
148.stat .value { display: block; color: #00ff88; font-size: 18px; margin-top: 4px; }
149.stat .value.ok { color: #00ff88; }
150ul { color: #c0c0c0; margin-top: 10px; }
151li { margin: 5px 0; }
152</style>

Widzisz błąd w tej lekcji?

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. Jak zdefiniować dynamiczny parametr w path Vue Router?

  2. 2. Jak uzyskać dostęp do parametrów aktualnego route w Composition API?

To 2 z 3 pytań do tej lekcji. Pozostałe rozwiążesz w grze.

Zadania praktyczne w grze

  • Edytor kodu

    Stwórz route /system/:id i komponent odczytujący params.id przez useRoute().

  • Układanie w pionie

    Uporządkuj kroki obsługi dynamicznego route:

  • Klikanie w kolejności

    Ułóż programatyczną nawigację z parametrami:

  • Edytor kodu

    Nawiguj z query params i odczytaj je w komponencie przez useRoute().query.

  • Układanie w poziomie

    Ułóż składnię RouterLink z dynamicznym parametrem:

Przydatne artykuły