Kurs Vue.js · Moduł 10: Vue Router
Dynamiczne Trasy
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. Jak zdefiniować dynamiczny parametr w path Vue Router?
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: