Skip to content

Kapitel 32: Vue Router (Teil 6) - Route-Guards

📙 Lernziel: Route-Guards (Navigationswächter) meistern!


32.1 Was sind Route-Guards?

Route-Guards ermöglichen es, Navigationen zu überwachen oder zu blockieren.

Anwendungsfälle:

  • ✅ Authentifizierung prüfen (Login erforderlich)
  • ✅ Berechtigungen prüfen (Admin-Bereich)
  • ✅ Daten laden vor Route-Eintritt
  • ✅ Formular-Änderungen speichern (Unsaved Changes)

Beispiel-Szenario:

/user/profile → Prüfe ob eingeloggt → Erlaube oder Redirect zu /login

32.2 beforeEach() - Global Guard

beforeEach() wird bei jeder Navigation ausgeführt.

Syntax:

javascript
router.beforeEach((to, from, next) => {
  // Logik
})

Beispiel (Authentifizierung):

javascript
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import Login from '../views/Login.vue'
import Dashboard from '../views/Dashboard.vue'

const routes = [
  { path: '/', component: Home },
  { path: '/login', component: Login },
  { 
    path: '/dashboard', 
    component: Dashboard,
    meta: { requiresAuth: true }  // Meta-Feld
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

// Global Guard (beforeEach)
router.beforeEach((to, from) => {
  const isLoggedIn = localStorage.getItem('token') !== null
  
  // Wenn Route Authentifizierung erfordert
  if (to.meta.requiresAuth && !isLoggedIn) {
    return { path: '/login' }  // Redirect zu Login
  }
  
  // Wenn eingeloggt und versucht /login zu besuchen
  if (to.path === '/login' && isLoggedIn) {
    return { path: '/dashboard' }  // Redirect zu Dashboard
  }
})

export default router

Wichtig:

  • to = Ziel-Route
  • from = Aktuelle Route
  • return { path: '/login' } = Redirect
  • return false = Navigation abbrechen

32.3 beforeResolve() - Global Guard

beforeResolve() wird ausgeführt, kurz bevor Navigation bestätigt wird (nach beforeEach()).

Beispiel (Daten laden):

javascript
// router/index.js
router.beforeResolve(async (to, from) => {
  // Daten für Route laden
  if (to.meta.requiresData) {
    try {
      const data = await fetchDataForRoute(to)
      to.meta.data = data
    } catch (error) {
      return { path: '/error' }
    }
  }
})

Unterschied beforeEach() vs beforeResolve():

  • beforeEach(): Vor allen Guards
  • beforeResolve(): Nach allen Guards (kurz vor Navigation)

32.4 afterEach() - Global Hook

afterEach() wird ausgeführt, nachdem Navigation abgeschlossen ist (kann Navigation nicht blockieren).

Beispiel (Analytics):

javascript
// router/index.js
router.afterEach((to, from) => {
  // Google Analytics (Seitenaufruf tracken)
  console.log(`Navigation: ${from.path} → ${to.path}`)
  
  // In echtem Projekt:
  // gtag('config', 'GA_MEASUREMENT_ID', {
  //   page_path: to.path
  // })
})

Wichtig:

  • ❌ Kann Navigation nicht blockieren
  • ✅ Gut für Analytics, Logging, etc.

32.5 Route-Guards (Pro Route)

beforeEnter() - Nur für spezifische Route.

Beispiel:

javascript
// router/index.js
const checkAdmin = (to, from) => {
  const userRole = localStorage.getItem('role')
  if (userRole !== 'admin') {
    return { path: '/unauthorized' }
  }
}

const routes = [
  {
    path: '/admin',
    component: AdminView,
    beforeEnter: checkAdmin  // Guard nur für diese Route
  },
  {
    path: '/dashboard',
    component: DashboardView,
    beforeEnter: (to, from) => {
      const isLoggedIn = localStorage.getItem('token') !== null
      if (!isLoggedIn) {
        return { path: '/login' }
      }
    }
  }
]

Vorteil:

  • ✅ Guard nur für spezifische Route
  • ✅ Bessere Organisation

32.6 In-Komponente Guards

In-Komponente Guards - Innerhalb von Vue-Komponenten.

Verfügbare Guards:

  1. beforeRouteEnter() - Vor Route-Eintritt (Komponente noch nicht erstellt)
  2. beforeRouteUpdate() - Wenn Route sich ändert (z.B. /user/1/user/2)
  3. beforeRouteLeave() - Vor Verlassen der Route

Beispiel:

vue
<!-- views/EditUser.vue -->
<script setup>
import { ref } from 'vue'
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'

const user = ref({ name: '', email: '' })
const hasUnsavedChanges = ref(false)

// Guard: Vor Verlassen der Route
onBeforeRouteLeave((to, from) => {
  if (hasUnsavedChanges.value) {
    const answer = window.confirm('Du hast ungespeicherte Änderungen. Wirklich verlassen?')
    if (!answer) {
      return false  // Navigation abbrechen
    }
  }
})

// Guard: Wenn Route sich ändert (z.B. /user/1 → /user/2)
onBeforeRouteUpdate((to, from) => {
  // Daten neu laden
  fetchUserData(to.params.id)
})
</script>

<template>
  <div>
    <h2>Benutzer bearbeiten</h2>
    <form @input="hasUnsavedChanges = true">
      <label>Name: <input v-model="user.name" /></label><br>
      <label>Email: <input v-model="user.email" /></label><br>
      <button @click="hasUnsavedChanges = false">Speichern</button>
    </form>
  </div>
</template>

Wichtig:

  • onBeforeRouteLeave() - Gutt für Unsaved Changes Warnung
  • onBeforeRouteUpdate() - Gutt für Dynamische Routes

32.7 Übung: Auth Guard implementieren

Aufgabe: Erstelle einen Guard, der prüft ob Benutzer eingeloggt ist.

Lösung:

1. Router konfigurieren (router/index.js):

javascript
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import Login from '../views/Login.vue'
import Dashboard from '../views/Dashboard.vue'
import Admin from '../views/Admin.vue'

const routes = [
  { path: '/', component: Home },
  { path: '/login', component: Login },
  { 
    path: '/dashboard', 
    component: Dashboard,
    meta: { requiresAuth: true }
  },
  { 
    path: '/admin', 
    component: Admin,
    meta: { requiresAuth: true, requiresAdmin: true }
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

// Auth Guard
router.beforeEach((to, from) => {
  const isLoggedIn = localStorage.getItem('token') !== null
  const userRole = localStorage.getItem('role')
  
  // Prüfe Authentifizierung
  if (to.meta.requiresAuth && !isLoggedIn) {
    return { path: '/login' }
  }
  
  // Prüfe Admin-Berechtigung
  if (to.meta.requiresAdmin && userRole !== 'admin') {
    return { path: '/dashboard' }
  }
  
  // Wenn eingeloggt und versucht /login zu besuchen
  if (to.path === '/login' && isLoggedIn) {
    return { path: '/dashboard' }
  }
})

export default router

2. Login-Komponente (views/Login.vue):

vue
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'

const router = useRouter()
const username = ref('')
const password = ref('')

const login = () => {
  // Simuliere Login
  if (username.value === 'admin' && password.value === '123') {
    localStorage.setItem('token', 'abc123')
    localStorage.setItem('role', 'admin')
    router.push('/dashboard')
  } else if (username.value === 'user' && password.value === '123') {
    localStorage.setItem('token', 'xyz789')
    localStorage.setItem('role', 'user')
    router.push('/dashboard')
  } else {
    alert('Falsches Passwort!')
  }
}

const logout = () => {
  localStorage.removeItem('token')
  localStorage.removeItem('role')
  router.push('/login')
}
</script>

<template>
  <div class="login">
    <h2>Login</h2>
    <div>
      <label>Benutzername: <input v-model="username" /></label><br>
      <label>Passwort: <input v-model="password" type="password" /></label><br>
      <button @click="login">Einloggen</button>
      <button @click="logout">Ausloggen</button>
    </div>
  </div>
</template>

<style scoped>
.login {
  max-width: 400px;
  margin: 0 auto;
  padding: 20px;
}

button {
  margin: 10px 5px;
  padding: 10px 20px;
  background: #42b883;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}
</style>

3. Dashboard-Komponente (views/Dashboard.vue):

vue
<script setup>
import { useRouter } from 'vue-router'

const router = useRouter()

const logout = () => {
  localStorage.removeItem('token')
  localStorage.removeItem('role')
  router.push('/login')
}
</script>

<template>
  <div class="dashboard">
    <h2>Dashboard</h2>
    <p>Willkommen im Dashboard!</p>
    <button @click="logout">Ausloggen</button>
    <router-link to="/admin">Zu Admin</router-link>
  </div>
</template>

4. Admin-Komponente (views/Admin.vue):

vue
<template>
  <div class="admin">
    <h2>Admin-Bereich</h2>
    <p>Nur für Admins!</p>
    <router-link to="/dashboard">Zurück zu Dashboard</router-link>
  </div>
</template>

✅ Zusammenfassung

In diesem Kapitel hast du gelernt:

  • ✅ Was Route-Guards sind
  • beforeEach() (Global Guard)
  • beforeResolve() (Global Guard)
  • afterEach() (Global Hook)
  • ✅ Route-Guards (Pro Route)
  • ✅ In-Komponente Guards
  • ✅ Praxis: Auth Guard implementieren

🎯 Nächster Schritt: In Kapitel 33 lernst du Pinia (State Management)!


← Zurück zu Kapitel 31: Redirects & 404Weiter zu Kapitel 33: Pinia Grundlagen →

Frei für alle Anfänger