Vue - Routing: Unterschied zwischen den Versionen
| (14 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt) | |||
| Zeile 1: | Zeile 1: | ||
| + | [[Vue.js]] | ||
| + | [[Vue - Snippets]] | ||
| + | |||
Mit Vue erstellt man oft sogenannte Single Page Applications. D.h. alle Ansichten (Views) sind technisch gesehen auf einer HTML-Seite. | Mit Vue erstellt man oft sogenannte Single Page Applications. D.h. alle Ansichten (Views) sind technisch gesehen auf einer HTML-Seite. | ||
| Zeile 118: | Zeile 121: | ||
//this.$router.forward; | //this.$router.forward; | ||
| − | === | + | === Dynamic routing - $route === |
In der Praxis möchte man Pfadsegmente oft dynamisch gestalten. Also z.b. die ID eines Datensatzes nutzen der dann angezeigt werden soll | In der Praxis möchte man Pfadsegmente oft dynamisch gestalten. Also z.b. die ID eines Datensatzes nutzen der dann angezeigt werden soll | ||
https:/meineDomain.de/teams/team-xyz | https:/meineDomain.de/teams/team-xyz | ||
| Zeile 164: | Zeile 167: | ||
In Kombination mit <router-link> und einem v-bind für das to Argument kann man so auch eine passende Navigation erzeugen: | In Kombination mit <router-link> und einem v-bind für das to Argument kann man so auch eine passende Navigation erzeugen: | ||
<router-link :to="'/teams/' + id">Members</router-link> | <router-link :to="'/teams/' + id">Members</router-link> | ||
| + | |||
| + | ''' Ein Problem mit <router-link> ''' | ||
| + | Wenn du auf einen dynamischen Link gewechselt hast z.B. auf | ||
| + | myDomain.de/teams/t3 | ||
| + | und von dort über einen Link | ||
| + | <router-link to="/teams/t2">Klick mich</router-link> | ||
| + | wechseln möchtest funktioniert dies hier nicht. Das liegt daran, dass wir den Parameter /t2 bzw. teamId im created() Hook verarbeiten. Dieser wird aber nur beim ersten mal wenn die Komponente erstellt wird aufgerufen. Da wir schon in der Komponente sind passiert das nicht erneut und der View wird nicht aktualisiert. | ||
| + | |||
| + | '''Lösung: Ein watcher''' | ||
| + | |||
| + | Zwar wird created() nicht aufgerufen aber das $route Objekt ändert sich sehr wohl. Daher können wir einfach einen Watcher einsetzen, der bei Änderungen des router Objekts anspricht: | ||
| + | <syntaxhighlight lang="javascript"> | ||
| + | watch: { | ||
| + | $route(newRoute){ | ||
| + | this.loadTeamMembers(newRoute); | ||
| + | } | ||
| + | } | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | Zusätzlich bauen wir eine Funktion loadTeamMembers() die wir in methods unterbringen, damit wir den Code nicht mehrmals verwenden müssen (siehe finaler Code unten). Im Watcher können wir die Funktion einfach aufrufen wen das $route Objekt sich ändert. | ||
| + | |||
| + | ==== route Parameter als prop ==== | ||
| + | |||
| + | Der Ansatz oben hat einen Nachteil: Bisher wird die '''Komponente nur über einen Aufruf einer bestimmten URL gerendert'''. Wenn sie als Teil einer Parent-Komponente gerendert wird funktioniert das nicht. Hier wäre es '''schöner eine Property nutzen zu können'''. Statt über this.§route könnten wir dann über this.teamId darauf zugreifen. | ||
| + | |||
| + | Das ist einfach möglich indem wir in der route Definition den Parameter props: true hinzufügen. | ||
| + | props: true | ||
| + | Er sorgt dafür, dass das Pfad-Segement in den props der Komponente zur Verfügung steht: | ||
| + | props: ['teamId'], | ||
| + | Auf diese Weise wird die Component vielseitiger einsetzbar und wird etwas einfacher. | ||
| + | === redirect, alias === | ||
| + | In der Router Konfiguration kann man auch Redirects einsetzen. Zum Beispiel um die Rootseite, die im Moment leer ist (bis auf die Navigation), auf die Teams-Liste zu leiten: | ||
| + | { path: '/', redirect: '/teams'}, | ||
| + | Dies sollte vor der redirect definition stehen. | ||
| + | |||
| + | Alternativ kann man in der TeamsList Component die Route Seite als Alias. Hierbei bleibt die URL die Selbe aber der Inhalt der Teamsliste wird angezeigt: | ||
| + | { path: '/teams', component: TeamsList, alias: '/' }, | ||
| + | |||
| + | === catch all === | ||
| + | Zu guter Letzt möchten wir alle urls abfangen die nicht gehandelt sind. Das kann man mit einem catch all machen. Vue aktzeptiert in der Router Konfiguration auch dynamische Parameter mit Regex Ausdrücken. Daher können wir schreiben | ||
| + | { path: '/:notFound(.*)', component: NotFound}, //.* is regex for any characters | ||
| + | //{ path: '/:notFound(.*)', redirect: '/teams'}, // redirect would be possible too | ||
| + | |||
| + | Das notFound kann auch einen anderen Namen bekommen. Wichtig ist dass wir hier eine Funktion an das path Objekt übergeben an die wir einen regulären Ausdruck übergeben. Diese Route sollte ganz am Ende stehen sonst würde sie die nachfolgenden Überschreiben. | ||
| + | |||
| + | === Nested Routes === | ||
| + | Bisher haben wir /teams und /teams/[teamsId] als separate Routen definiert. Man kann aber die id Route auch als Kind der teams Route definieren. Das hat ein paar Vorteile. | ||
| + | * Der Router erkennt die Route als Kind und kann allen Parents eine Klasse und dem Kind eine extra Klasse vergeben. | ||
| + | * Man kann in der Parent Komponent die Kindkomponente als zusätzlichen Content anzeigen. sprich die Members werden zusätzlich in der Teams Liste angezeigt (dazu muss ein zusätzliches <router-view> in die Liste eingefügt werden, damit Vue weiß an welcher Stelle). | ||
| + | |||
| + | So gehts: | ||
| + | <syntaxhighlight lang="javascript"> | ||
| + | { path: '/teams', component: TeamsList, children: [ | ||
| + | { path: ':teamId', component: TeamMembers, props: true }, | ||
| + | ] }, | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | === Named Routes === | ||
| + | Zusätzlich kann man Routes auch ein name property geben. | ||
| + | |||
| + | <syntaxhighlight lang="javascript"> | ||
| + | { name: 'teams', path: '/teams', component: TeamsList, children: [ | ||
| + | { name: 'team-members', path: ':teamId', component: TeamMembers, props: true }, | ||
| + | </syntaxhighlight> | ||
| + | Dann kann man Links auch damit aufrufen. | ||
| + | <syntaxhighlight lang="html5"> | ||
| + | <router-link :to="memberPath">Members</router-link> | ||
| + | ... | ||
| + | computed: { | ||
| + | memberPath(){ | ||
| + | //return '/teams/' + this.id; | ||
| + | return { | ||
| + | name: 'team-members', params: { teamId: this.id } | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | </syntaxhighlight> | ||
| + | In diesem Beispiel wird noch das params Argument benötigt, da wir in der Komponente die teamId übergeben müssen. Wir haben jetzt etwas mehr Code aber wir können den Code besser Warten. Wenn sich das Segment "teams" ändert müssen wir das nur noch in der Route Konfiguration machen. Das kann bei größeren Apps eine Überlegung wert sein. | ||
| + | |||
| + | === URL Query Parameters === | ||
| + | Auch GET artige Parameter kann man erzeugen (obwohl das eigentlich keine GET Parameter sind, da wir ja keine GET Anfrage an den Server schicken. Aber man kann Parameter an die URL hängen: | ||
| + | |||
| + | <syntaxhighlight lang="javascript"> | ||
| + | return { | ||
| + | //name: 'team-members', params: { teamId: this.id } | ||
| + | name: 'team-members', | ||
| + | params: { teamId: this.id }, | ||
| + | query: {sort: 'asc'} | ||
| + | } | ||
| + | </syntaxhighlight> | ||
| + | Und sie in einer Komponente wieder auswerten: | ||
| + | console.log(this.$route.query); | ||
| + | |||
| + | == Multiple Routes == | ||
| + | Man kann in einer Komponente mehrere <router-view> Tags mit jeweils einer eigenen Komponente nutzen. Dazu muss man | ||
| + | * Namen für die router-views vergeben also z.b. in App.vue zusätzlich den router-view | ||
| + | <router-view name="footer"></router-view> | ||
| + | * in der Router Konfiguration statt dem component, das components (mitn s) Objekt ersetzen und die Komponentennamen definieren. Dabei übergibt man ein Objekt mit den Namen und den Views bzw. Komponenten | ||
| + | components:{ default: TeamsList, footer: TeamsFooter}, | ||
| + | |||
| + | == Controlling Scrolling Behaviour - scrollBehaviour == | ||
| + | Es gibt ein weiteres Objekt für die Konfiguration des Routers. Das scrollBehaviour Objekt. Es ist kein Kindobjekt von routes sondern ein eigenständiges Objekt wie z.b. history. scrollBehaviour eine Funktion die automatisch 3 Argumente bekommt nämlich auf welcher Seite der User gelandet ist, wo er her kam und an welcher Stelle er zuletzt gescrollt hat. Als Rückgabewert gibt man ein Objekt zurück das angibt wohin man scrollen möchte: | ||
| + | |||
| + | <syntaxhighlight lang="javascript"> | ||
| + | scrollBehavior(to, from, savedPosition){ | ||
| + | // to is where we navigate to (route object), | ||
| + | // from page where we came from | ||
| + | // savePosition stores our last page offset | ||
| + | // this function should return where to scroll | ||
| + | |||
| + | // console.log(to, from, savedPosition) | ||
| + | |||
| + | if (savedPosition){ | ||
| + | return savedPosition; | ||
| + | }else{ | ||
| + | return {left: 0, top: 0} | ||
| + | } | ||
| + | } | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | == Navigation Guard == | ||
| + | Navigation Guards sind im Prinzip Hooks, die getriggert werden wenn eine Navigation stattfindet. | ||
| + | === beforeEach === | ||
| + | Es gibt den Navigation Guard in Form der Funktion '''beforeEach'''. Das ist im Prinzip ein Hook. Dieser wird getriggert sobald eine Navigation stattfindet (die Seite sich ändert) und ermöglicht es in die Ausführung einzugreifen. Dies kann man z.B für '''Authentication''' nutzen, oder um '''Änderungen zu tracken''', oder um zu verhindern, dass ein Benutzer die '''Seite ausversehen wechselt''' bevor er gespeichert hat. | ||
| + | |||
| + | Die Guard Funktion wird aufgerufen sobald eine Navigation stattfindet (Link klicken oder Seite manuell laden) Übergeben werden neben den to und from Argumenten noch ein next argument (Namen sind frei wählbar, Reihenfolge ist wichtig). | ||
| + | |||
| + | <syntaxhighlight lang="javascript"> | ||
| + | // Navigationm Guard | ||
| + | router.beforeEach(function(to, from, next) { | ||
| + | console.log("Global beforeEach..."); | ||
| + | console.log(to, from); | ||
| + | next(); // means true will allow navigation, false will cancel | ||
| + | //next(false); | ||
| + | //next('/teams'); | ||
| + | //next({ name: 'team-members', params: { teamId: 't2'} }) | ||
| + | }); | ||
| + | </syntaxhighlight> | ||
| + | next entscheidet, ob die Navigation ausgeführt wird oder nicht. Next enthält true, false oder ein Ziel in Form eines Strings oder Route Objekts. Default ist true wenn kein Parameter angegeben ist. | ||
| + | |||
| + | === afterEach() === | ||
| + | Es gibt auch einen Hook, der nach der Navigation ausgeführt wird. Den afterEach() Navigation Guard: | ||
| + | <syntaxhighlight lang="javascript"> | ||
| + | router.afterEach(function(zu,von){ | ||
| + | console.log('Global afterEach hook'); | ||
| + | console.log(zu, von); | ||
| + | }); | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | Da er nach der Navigation ausgeführt wird benötigt er kein next Argument. Er eignet sich z.b. um Analytics Infos oder Protokolldaten an den Server zu senden. | ||
| + | |||
| + | === Globale und lokale Navigation Guards === | ||
| + | die Funktion beforeEnter kann man auf der router Ebene oder auf der Ebene der einzelnen routes einsetzen. Je nachdem wird sie nur aufgerufen wenn die Route gerendert wird, oder wenn global bei jeder Änderung der Seite (auch beim ersten Aufruf) | ||
| + | |||
| + | Zusätzlich kannst Du lokale Guards auch in einer Komponente definieren. Dann schreibst du | ||
| + | |||
| + | Für Komponenten die z.B. über ein for Schleife wiederverwendet werden gibt es noch den | ||
| + | '''beforeRouteUpdate''' Hook. | ||
| + | |||
| + | === beforeRouteLeave === | ||
| + | Der beforeRouteLeave Guard ist z.B. nützlich wenn Du verhindern möchtest, dass ein Benutzer '''die Seite versehentlich verläßt'''. Er wird vor allen anderen Guards getriggert. | ||
| + | |||
| + | <syntaxhighlight lang="javascript"> | ||
| + | beforeRouteLeave(to, from, next){ | ||
| + | console.log("beforeRouteLeave in UsersList.vue: "); | ||
| + | console.log(to, from); | ||
| + | if (this.changesSaved) { | ||
| + | next(); | ||
| + | }else{ | ||
| + | const userWantsToLeave = confirm('Are you sure you want to quit without saving?'); | ||
| + | next(userWantsToLeave); | ||
| + | } | ||
| + | } | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | == Meta == | ||
| + | Im meta Objekt kann man den einzelenen routes weitere Eigenschaften mitgeben. Das ist oft nützlich im Zusammenhang mit Guards. | ||
| + | |||
| + | <syntaxhighlight lang="javascript"> | ||
| + | meta: { needsAuth: true }, | ||
| + | //... | ||
| + | router.beforeEach(function(to, from, next) { | ||
| + | if (to.meta.meedsAuth) { | ||
| + | console.log("Needs Auth!"); | ||
| + | } | ||
| + | //... | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | == Datei Organisation == | ||
| + | Bei größeren Projekten kann es sinnvoll sein alle Components die man über Router ansteuert in einen | ||
| + | /pages | ||
| + | Ordner zu packen, so dass man sie leicht findet. | ||
| + | |||
| + | Außerdam kann die main.js sehr groß werden, deshalb kann es sinnvoll sein eine eigene Datei router.js für die Router Zeilen zu nutzen. Dort kannst du dann per export alles exportieren und wieder in main.js importieren: | ||
| + | |||
| + | <syntaxhighlight lang="javascript"> | ||
| + | // main.js | ||
| + | import { createApp } from 'vue'; | ||
| + | import App from './App.vue'; | ||
| + | const app = createApp(App) | ||
| + | |||
| + | import router from './router.js'; | ||
| + | app.use(router); | ||
| + | |||
| + | app.mount('#app'); | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | router.js | ||
| + | <syntaxhighlight lang="javascript"> | ||
| + | import { createRouter, createWebHistory } from 'vue-router'; | ||
| + | import TeamsList from './components/teams/TeamsList.vue'; | ||
| + | //... | ||
| + | |||
| + | const router = createRouter({//...}); | ||
| + | //... | ||
| + | export default router; // export stuff so that we can import it in main.js | ||
| + | </syntaxhighlight> | ||
Aktuelle Version vom 11. Januar 2021, 17:21 Uhr
Vue.js Vue - Snippets
Mit Vue erstellt man oft sogenannte Single Page Applications. D.h. alle Ansichten (Views) sind technisch gesehen auf einer HTML-Seite.
Auch wenn ich zwischen Komponenten oder Ansichten wechlse bleibt daher die URL der Seite immer gleich. Wenn man den Link teilt, landert der User immer auf der Startseite.
Mit dem Routing von Vue kann man für Views eigene URLs zur Verfügung stellen. So dass jede Ansicht der App mit einer eigenen URL repräsentiert werden kann.
Routing in Vue funktioniert mit dem vue-router. Dies ist ein zusätzliches Package des Entwickler Teams, das zunächst in Deinem Projekt installiert werden muss:
npm install --save vue-router
Wenn es später "not found" compiler warnings gibt versuche:
npm install --save vue-router@next
Router und Routes registrieren[Bearbeiten]
Ein Router läßt sich so erzeugen: main.js
//import { createRouter } from 'vue-router'; // without history
import { createRouter, createWebHistory } from 'vue-router';
const router = createRouter();
In der app läßt er sich mit use nutzen:
app.use(router)
wobei app eine Vue Instanz ist.
Zusätzlich zum router haben wir noch das createWebHistory importiert. Damit kann Vue die BrowserHistory auslesen.
Der Router benötigt eine Konfiguration in der wir zumindest die history und die routes bestimmen:
const router = createRouter({
history: createWebHistory(), // we use built in browser support
routes: []
});
Das routes array bekommt später alle Routes die wir nutzen möchten.
Ziel ist im Prinzip eine eigene URL für bestimmte Components zu generieren. Eine Route benötigt also zumindest ein paar Informationen
- einen Pfad (path) für die URL
- eine Komponente die gerendert werden soll
- eine Information an welcher Stelle des Views die Komponente gerendert werden soll.
Pfad und Komponente lassen sich in der routes Konfiguration angeben. Damit sind unser main.js Code jetzt so aus:
main.js
import { createApp } from 'vue';
import { createRouter, createWebHistory } from 'vue-router';
import App from './App.vue';
import TeamsList from './components/teams/TeamsList.vue';
import UsersList from './components/users/UsersList.vue';
const router = createRouter({
history: createWebHistory(), // we use built in browser support
routes: [
{ path: '/teams', component: TeamsList },
{ path: '/users', component: UsersList }
]
});
const app = createApp(App)
app.use(router);
app.mount('#app');
<router-view>[Bearbeiten]
Nun fehlt noch die Ansicht und die Information wo diese gerendert werden soll. Ohne Routing macht man Ansichten oft über dynamische Komponenten und den <component> Tag.
<component :is="activePage"></component>
Der vue-router stellt eine eigene component für diesen Zweck zur Verfügung der anstelle verwendet werden kann den router-view:
<router-view></router-view>
Damit ändert sich auch einiges am Code der Komponente. Die in main.js importierten und in routes registrierten Komponenten stehen automatisch zur Verfügung und müssen nicht mehr global oder lokal importiert und auch nicht mehr registriert werden. App.vue
<template>
<the-navigation @set-page="setActivePage"></the-navigation>
<main>
<router-view></router-view>
</main>
</template>
Wenn wir jetzt die Seite mit
myDomain.de/teams
aufrufen wird an der Stelle von router-view die Komponente TeamsList.vue gerendert.
[Bearbeiten]
Wenn wir eine Navigation in der Seite haben muss diese ebenfalls anders arbeiten. Klassischerweise hat man eine Property z.b. active Page, die man über buttons o.ä. setzt und darüber eine dynamische <component> steuert. Mit dabei sind dann oftmals custom events um über mehrere Komponenten hinweg die Variable zu setzen und methods ...
Mit dem router können wir theoretisch einfach Links setzen, vue-router hat aber eine spezielle Komponente die wir einfach einsetzen können.
<template>
<header>
<nav>
<ul>
<li>
<router-link to="/teams">Teams</router-link>
</li>
<li>
<router-link to="/users">Users</router-link>
</li>
</ul>
</nav>
</header>
</template>
Zusätzlich liefert router-links auch noch die Klassen router-link-active und router-link-exact-active die man für das Styling nutzen kann. Die Klassennamen lassen sich auch im router-Konfigurationsobjekt anpassen. Z.B
linkActiveClass: 'active'
Dynamische Links[Bearbeiten]
Für das to Argument können wir auch v-bind nutzen. So kann man es an Properties binden:
<router-link :to="'/teams/' + id">Members</router-link>
Programmatic routing / $router[Bearbeiten]
Über das $router special property kann man auch über den Programmcode navigieren:
this.$router.push('/teams');
//this.$router.back;
//this.$router.forward;
Dynamic routing - $route[Bearbeiten]
In der Praxis möchte man Pfadsegmente oft dynamisch gestalten. Also z.b. die ID eines Datensatzes nutzen der dann angezeigt werden soll
https:/meineDomain.de/teams/team-xyz
Hier soll z.B. das Team mit der ID team-xyz angezeigt werden.
Dynamic Route definieren
In der Routing Konfiguration gibt man dafür eine dynamic route mit einem route parameter an.
{ path: '/teams/:teamId', component: TeamMembers },
Mit dem Doppelpunkt wird der Parameter gekennzeichnet. Wenn auf der gleichen Ebene noch ein weiteres nicht-dynamischen Segement verwendet wird muss das vor dem dynamischen stehen.
{ path: '/teams/new', component: NewMember },
{ path: '/teams/:teamId', component: TeamMembers },
Dynamic Route einsetzen Wir gehen davon aus dass wir in App.vue Daten in dieser Art haben:
teams: [
{ id: 't1', name: 'Frontend Engineers', members: ['u1', 'u2'] },
{ id: 't2', name: 'Backend Engineers', members: ['u1', 'u2', 'u3'] },
{ id: 't3', name: 'Client Consulting', members: ['u4', 'u5'] },
],
Diese werden provided, so dass wir sie in einer Kindkomponente injecten können:
provide() {
return {
teams: this.teams,
};
},
TeamMembers.vue Hier sollen die Daten angezeigt werden. Damit die Daten zur Verfügung stehen injecten wir:
inject: ['teams'],
Als nächstes müssen wir herausfinden welches Segement in der URL steht. Das können wir über einen Hook machen.
created(){}
wird ausgeführt, nachdem die Komponente erzeugt wurde, aber bevor sie gerendert wird.
Im Hook können wir über das $route Objekt auf die aktuelle route zugreifen.
console.log($this.route); // shows all route things in the console this.$route.path; // /teams/t1 this.$route.params.teamId; //t1
In Kombination mit <router-link> und einem v-bind für das to Argument kann man so auch eine passende Navigation erzeugen:
<router-link :to="'/teams/' + id">Members</router-link>
Ein Problem mit <router-link> Wenn du auf einen dynamischen Link gewechselt hast z.B. auf
myDomain.de/teams/t3
und von dort über einen Link
<router-link to="/teams/t2">Klick mich</router-link>
wechseln möchtest funktioniert dies hier nicht. Das liegt daran, dass wir den Parameter /t2 bzw. teamId im created() Hook verarbeiten. Dieser wird aber nur beim ersten mal wenn die Komponente erstellt wird aufgerufen. Da wir schon in der Komponente sind passiert das nicht erneut und der View wird nicht aktualisiert.
Lösung: Ein watcher
Zwar wird created() nicht aufgerufen aber das $route Objekt ändert sich sehr wohl. Daher können wir einfach einen Watcher einsetzen, der bei Änderungen des router Objekts anspricht:
watch: {
$route(newRoute){
this.loadTeamMembers(newRoute);
}
}
Zusätzlich bauen wir eine Funktion loadTeamMembers() die wir in methods unterbringen, damit wir den Code nicht mehrmals verwenden müssen (siehe finaler Code unten). Im Watcher können wir die Funktion einfach aufrufen wen das $route Objekt sich ändert.
route Parameter als prop[Bearbeiten]
Der Ansatz oben hat einen Nachteil: Bisher wird die Komponente nur über einen Aufruf einer bestimmten URL gerendert. Wenn sie als Teil einer Parent-Komponente gerendert wird funktioniert das nicht. Hier wäre es schöner eine Property nutzen zu können. Statt über this.§route könnten wir dann über this.teamId darauf zugreifen.
Das ist einfach möglich indem wir in der route Definition den Parameter props: true hinzufügen.
props: true
Er sorgt dafür, dass das Pfad-Segement in den props der Komponente zur Verfügung steht:
props: ['teamId'],
Auf diese Weise wird die Component vielseitiger einsetzbar und wird etwas einfacher.
redirect, alias[Bearbeiten]
In der Router Konfiguration kann man auch Redirects einsetzen. Zum Beispiel um die Rootseite, die im Moment leer ist (bis auf die Navigation), auf die Teams-Liste zu leiten:
{ path: '/', redirect: '/teams'},
Dies sollte vor der redirect definition stehen.
Alternativ kann man in der TeamsList Component die Route Seite als Alias. Hierbei bleibt die URL die Selbe aber der Inhalt der Teamsliste wird angezeigt:
{ path: '/teams', component: TeamsList, alias: '/' },
catch all[Bearbeiten]
Zu guter Letzt möchten wir alle urls abfangen die nicht gehandelt sind. Das kann man mit einem catch all machen. Vue aktzeptiert in der Router Konfiguration auch dynamische Parameter mit Regex Ausdrücken. Daher können wir schreiben
{ path: '/:notFound(.*)', component: NotFound}, //.* is regex for any characters
//{ path: '/:notFound(.*)', redirect: '/teams'}, // redirect would be possible too
Das notFound kann auch einen anderen Namen bekommen. Wichtig ist dass wir hier eine Funktion an das path Objekt übergeben an die wir einen regulären Ausdruck übergeben. Diese Route sollte ganz am Ende stehen sonst würde sie die nachfolgenden Überschreiben.
Nested Routes[Bearbeiten]
Bisher haben wir /teams und /teams/[teamsId] als separate Routen definiert. Man kann aber die id Route auch als Kind der teams Route definieren. Das hat ein paar Vorteile.
- Der Router erkennt die Route als Kind und kann allen Parents eine Klasse und dem Kind eine extra Klasse vergeben.
- Man kann in der Parent Komponent die Kindkomponente als zusätzlichen Content anzeigen. sprich die Members werden zusätzlich in der Teams Liste angezeigt (dazu muss ein zusätzliches <router-view> in die Liste eingefügt werden, damit Vue weiß an welcher Stelle).
So gehts:
{ path: '/teams', component: TeamsList, children: [
{ path: ':teamId', component: TeamMembers, props: true },
] },
Named Routes[Bearbeiten]
Zusätzlich kann man Routes auch ein name property geben.
{ name: 'teams', path: '/teams', component: TeamsList, children: [
{ name: 'team-members', path: ':teamId', component: TeamMembers, props: true },
Dann kann man Links auch damit aufrufen.
<router-link :to="memberPath">Members</router-link>
...
computed: {
memberPath(){
//return '/teams/' + this.id;
return {
name: 'team-members', params: { teamId: this.id }
}
}
}
In diesem Beispiel wird noch das params Argument benötigt, da wir in der Komponente die teamId übergeben müssen. Wir haben jetzt etwas mehr Code aber wir können den Code besser Warten. Wenn sich das Segment "teams" ändert müssen wir das nur noch in der Route Konfiguration machen. Das kann bei größeren Apps eine Überlegung wert sein.
URL Query Parameters[Bearbeiten]
Auch GET artige Parameter kann man erzeugen (obwohl das eigentlich keine GET Parameter sind, da wir ja keine GET Anfrage an den Server schicken. Aber man kann Parameter an die URL hängen:
return {
//name: 'team-members', params: { teamId: this.id }
name: 'team-members',
params: { teamId: this.id },
query: {sort: 'asc'}
}
Und sie in einer Komponente wieder auswerten:
console.log(this.$route.query);
Multiple Routes[Bearbeiten]
Man kann in einer Komponente mehrere <router-view> Tags mit jeweils einer eigenen Komponente nutzen. Dazu muss man
- Namen für die router-views vergeben also z.b. in App.vue zusätzlich den router-view
<router-view name="footer"></router-view>
- in der Router Konfiguration statt dem component, das components (mitn s) Objekt ersetzen und die Komponentennamen definieren. Dabei übergibt man ein Objekt mit den Namen und den Views bzw. Komponenten
components:{ default: TeamsList, footer: TeamsFooter},
Controlling Scrolling Behaviour - scrollBehaviour[Bearbeiten]
Es gibt ein weiteres Objekt für die Konfiguration des Routers. Das scrollBehaviour Objekt. Es ist kein Kindobjekt von routes sondern ein eigenständiges Objekt wie z.b. history. scrollBehaviour eine Funktion die automatisch 3 Argumente bekommt nämlich auf welcher Seite der User gelandet ist, wo er her kam und an welcher Stelle er zuletzt gescrollt hat. Als Rückgabewert gibt man ein Objekt zurück das angibt wohin man scrollen möchte:
scrollBehavior(to, from, savedPosition){
// to is where we navigate to (route object),
// from page where we came from
// savePosition stores our last page offset
// this function should return where to scroll
// console.log(to, from, savedPosition)
if (savedPosition){
return savedPosition;
}else{
return {left: 0, top: 0}
}
}
[Bearbeiten]
Navigation Guards sind im Prinzip Hooks, die getriggert werden wenn eine Navigation stattfindet.
beforeEach[Bearbeiten]
Es gibt den Navigation Guard in Form der Funktion beforeEach. Das ist im Prinzip ein Hook. Dieser wird getriggert sobald eine Navigation stattfindet (die Seite sich ändert) und ermöglicht es in die Ausführung einzugreifen. Dies kann man z.B für Authentication nutzen, oder um Änderungen zu tracken, oder um zu verhindern, dass ein Benutzer die Seite ausversehen wechselt bevor er gespeichert hat.
Die Guard Funktion wird aufgerufen sobald eine Navigation stattfindet (Link klicken oder Seite manuell laden) Übergeben werden neben den to und from Argumenten noch ein next argument (Namen sind frei wählbar, Reihenfolge ist wichtig).
// Navigationm Guard
router.beforeEach(function(to, from, next) {
console.log("Global beforeEach...");
console.log(to, from);
next(); // means true will allow navigation, false will cancel
//next(false);
//next('/teams');
//next({ name: 'team-members', params: { teamId: 't2'} })
});
next entscheidet, ob die Navigation ausgeführt wird oder nicht. Next enthält true, false oder ein Ziel in Form eines Strings oder Route Objekts. Default ist true wenn kein Parameter angegeben ist.
afterEach()[Bearbeiten]
Es gibt auch einen Hook, der nach der Navigation ausgeführt wird. Den afterEach() Navigation Guard:
router.afterEach(function(zu,von){
console.log('Global afterEach hook');
console.log(zu, von);
});
Da er nach der Navigation ausgeführt wird benötigt er kein next Argument. Er eignet sich z.b. um Analytics Infos oder Protokolldaten an den Server zu senden.
[Bearbeiten]
die Funktion beforeEnter kann man auf der router Ebene oder auf der Ebene der einzelnen routes einsetzen. Je nachdem wird sie nur aufgerufen wenn die Route gerendert wird, oder wenn global bei jeder Änderung der Seite (auch beim ersten Aufruf)
Zusätzlich kannst Du lokale Guards auch in einer Komponente definieren. Dann schreibst du
Für Komponenten die z.B. über ein for Schleife wiederverwendet werden gibt es noch den beforeRouteUpdate Hook.
beforeRouteLeave[Bearbeiten]
Der beforeRouteLeave Guard ist z.B. nützlich wenn Du verhindern möchtest, dass ein Benutzer die Seite versehentlich verläßt. Er wird vor allen anderen Guards getriggert.
beforeRouteLeave(to, from, next){
console.log("beforeRouteLeave in UsersList.vue: ");
console.log(to, from);
if (this.changesSaved) {
next();
}else{
const userWantsToLeave = confirm('Are you sure you want to quit without saving?');
next(userWantsToLeave);
}
}
Meta[Bearbeiten]
Im meta Objekt kann man den einzelenen routes weitere Eigenschaften mitgeben. Das ist oft nützlich im Zusammenhang mit Guards.
meta: { needsAuth: true },
//...
router.beforeEach(function(to, from, next) {
if (to.meta.meedsAuth) {
console.log("Needs Auth!");
}
//...
Datei Organisation[Bearbeiten]
Bei größeren Projekten kann es sinnvoll sein alle Components die man über Router ansteuert in einen
/pages
Ordner zu packen, so dass man sie leicht findet.
Außerdam kann die main.js sehr groß werden, deshalb kann es sinnvoll sein eine eigene Datei router.js für die Router Zeilen zu nutzen. Dort kannst du dann per export alles exportieren und wieder in main.js importieren:
// main.js
import { createApp } from 'vue';
import App from './App.vue';
const app = createApp(App)
import router from './router.js';
app.use(router);
app.mount('#app');
router.js
import { createRouter, createWebHistory } from 'vue-router';
import TeamsList from './components/teams/TeamsList.vue';
//...
const router = createRouter({//...});
//...
export default router; // export stuff so that we can import it in main.js