Routennavigation

In diesem Kapitel lernst du, wie du mit React Router v6 eine Seitennavigation ohne Neuladen implementierst, einschließlich dynamischer Routen und dem Auslesen von Parametern.


Warum Routing verwenden?

Traditionelle Multi-Page-Websites fordern bei jedem Klick auf einen Link eine neue Seite vom Server an, was zu Weißbildschirm-Flackern führt.

Frontend-RoutingEs lässt JavaScript steuern, welche Komponente angezeigt wird, wenn sich die URL ändert, ohne Anfragen an den Server zu senden.

Vorteile:Reibungsloses Umschalten, das Erlebnis fühlt sich wie eine native App an。


Kernkonzepte von React Router v6

KonzeptZweckEntsprechender Code
RoutentabelleDefiniert die Zuordnung zwischen URLs und Komponenten<Routes> + <Route>
Route-OutletPlatzhalter, in dem untergeordnete Routen angezeigt werden<Outlet />
NavigationslinkZum Navigieren klicken<Link to="...">
Dynamische RouteVariable in der URL/post/:id
Parameter lesenDen Wert aus der dynamischen Route abrufenuseParams()

Installation und Konfiguration

$ npm install react-router-dom@6

Route-Konfiguration erstellen

Beispiel

// Dateipfad: src/router/index.jsx
import { createBrowserRouter } from 'react-router-dom'
import App from '../App'
import HomePage from '../pages/HomePage'
import PostPage from '../pages/PostPage'

// createBrowserRouter erstellt eine Router-Instanz
const router = createBrowserRouter([
  {
    path: '/',                // App fungiert als Root-Layout
    element: <App />,
    children: [
      {
        index: true,          // index: true bedeutet die Standard-Kindroute für den Pfad /
        element: <HomePage />
      },
      {
        path: 'post/:id',     // Dynamische Route: :id stimmt mit jedem Wert überein
        element: <PostPage />
      }
    ]
  }
])

export default router

Zwei Router-Modi:createBrowserRouterErzeugt saubere URLs (/post/1), erfordert eine Serverkonfiguration. Wenn die Bereitstellungsumgebung ungewiss ist, verwendecreateHashRouter, die URL enthält ein # (/#/post/1), und es ist keine Serverkonfiguration erforderlich.

Registriere den Router in main.jsx

Beispiel

// Dateipfad: src/main.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import { RouterProvider } from 'react-router-dom'
import router from './router'
import './index.css'

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    {/* RouterProvider injiziert den Router in die App */}
    <RouterProvider router={router} />
  </React.StrictMode>
)

Verwende Outlet in App.jsx

Beispiel

// Dateipfad: src/App.jsx
import { Outlet } from 'react-router-dom'
import NavBar from './components/NavBar'
import './App.css'

function App() {
  return (
    <div className="app">
      <NavBar />
      <main className="container">
        {/* Outlet ist die Ausgabestelle für verschachtelte Routen: Die von Kindrouten gematchte Komponente wird hier gerendert */}
        <Outlet />
      </main>
      <footer className="footer">
        <p>© 2024 BEISPIEL Blog. Betrieben mit React.</p>
      </footer>
    </div>
  )
}

export default App

Link — Deklarative Navigation

LinkErsetzt den traditionellen<a>Tag und löst keinen Seiten-Refresh aus.

Beispiel

import { Link } from 'react-router-dom'

function BlogCard({ id, title, summary, date, category }) {
  return (
    <Link to={`/post/${id}`} className="card-link">
      <div className="card">
        <span className="tag">{Kategorie}</span>
        <h3>{title}</h3>
        <p>{summary}</p>
        <span className="date">{Datum}</span>
      </div>
    </Link>
  )
}

Verwende nicht<a href="....html">— es bewirkt, dass der Browser eine neue Seite vom Server anfordert, und die gesamte React-App wird neu initialisiert. Verwende<Link to="...">um auf der Client-Seite ohne Aktualisierung umzuschalten.


useParams — Routenparameter lesen

Auf der Detailseite müssen Sie die Artikel-ID aus der URL extrahieren, um den entsprechenden Artikel zu finden.

Beispiel

import { useParams } from 'react-router-dom'

function PostPage() {
  // useParams gibt ein Objekt zurück, das die Parameter in der URL enthält
  const { id } = useParams()
  // Hinweis: id ist ein String; wenn Sie eine Zahl benötigen, verwenden Sie Number(id), um sie umzuwandeln

  return (
    <div>
      <p>Aktuelle Artikel-ID:{id}</p>
    </div>
  )
}

useNavigate — Programmatische Navigation

In manchen Szenarien müssen Sie navigieren, ohne auf einen Link zu klicken (z. B. nach dem Absenden eines Formulars oder wenn ein Artikel nicht gefunden wird, zurück zur Startseite navigieren).

Beispiel

import { useNavigate } from 'react-router-dom'

function PostPage() {
  const navigate = useNavigate()

  const article = null  // Angenommen, der Artikel wird nicht gefunden

  if (!article) {
    // Wenn der Artikel nicht existiert, zur Startseite navigieren
    return (
      <div>
        <p>Artikel nicht gefunden</p>
        <button onClick={() => navigate('/')}>Zurück zur Startseite</button>
      </div>
    )
  }

  return <div>Artikeldetails...</div>
}

Vue3 vs React Router API-Vergleich

MerkmalVue3React
Navigationslink<RouterLink><Link>
Route-Outlet<RouterView><Outlet>
Dynamische Route/post/:id/post/:id
Parameter lesenuseRoute().paramsuseParams()
Programmatische NavigationuseRouter().push()useNavigate()

Praxis: Vollständige Detailseite + Router-Integration

Schritt 1: BlogCard ändern, um Klick-Navigation zu unterstützen

Beispiel

// Dateipfad: src/components/BlogCard.jsx
import { Link } from 'react-router-dom'

function BlogCard({ id, title, summary, date, category }) {
  return (
    <Link to={`/post/${id}`} className="card-link">
      <div className="card">
        <span className="tag">{Kategorie}</span>
        <h3>{title}</h3>
        <p>{summary}</p>
        <span className="date">{Datum}</span>
      </div>
    </Link>
  )
}

export default BlogCard

Schritt 2: HomePage und PostPage erstellen

Beispiel

// Dateipfad: src/pages/HomePage.jsx
import { useState, useMemo } from 'react'
import BlogCard from '../components/BlogCard'
import CategoryFilter from '../components/CategoryFilter'

function HomePage() {
  const [articles] = useState([
    { id: 1, title: 'Der komplette Leitfaden zu React für Anfänger', summary: 'React von Grund auf lernen', category: 'React', date: '2024-05-10',
      content: '<h2>Warum React lernen?</h2><p>React ist derzeit eines der beliebtesten Frontend-Frameworks...</p>' },
    { id: 2, title: 'JS-Async-Programmierung erklärt', summary: 'Promise und async/await verstehen', category: 'JavaScript', date: '2024-05-08',
      content: '<h2>Was ist Async?</h2><p>JS ist single-threaded; asynchrone Vorgänge können den Haupt-Thread freihalten...</p>' },
    { id: 3, title: 'CSS-Grid-Layout-Übung', summary: 'Mit Grid responsive Layouts erstellen', category: 'CSS', date: '2024-05-05',
      content: '<h2>Erste Schritte mit Grid</h2><p>Grid ist ein zweidimensionales Layout-System...</p>' },
  ])

  const [activeCategory, setActiveCategory] = useState('Alle')

  const categories = useMemo(() => {
    return ['Alle', ...new Set(articles.map(a => a.category))]
  }, [articles])

  const filteredArticles = useMemo(() => {
    if (activeCategory === 'All') return articles
    return articles.filter(a => a.category === activeCategory)
  }, [articles, activeCategory])

  return (
    <div>
      <h2 className="section-title">Neueste Artikel</h2>
      <CategoryFilter
        categories={categories}
        activeCategory={activeCategory}
        onCategoryChange={setActiveCategory}
      />
      <p className="result-info">common{filteredArticles.length}Artikel</p>
      {filteredArticles.length === 0 ? (
        <p className="empty-tip">Noch keine Artikel in dieser Kategorie</p>
      ) : (
        <div className="article-grid">
          {filteredArticles.map(article => (
            <BlogCard key={article.id} {...article} />
          ))}
        </div>
      )}
    </div>
  )
}

export default HomePage

Beispiel

// Dateipfad: src/pages/PostPage.jsx
import { useParams, Link } from 'react-router-dom'
import { useMemo } from 'react'

// Artikeldaten (werden in späteren Kapiteln aus externen Quellen geladen)
const articles = [
  { id: 1, title: 'Der vollständige Leitfaden zu React für Anfänger', category: 'React', date: '2024-05-10',
    content: '<h2>Warum React lernen?</h2><p>React ist derzeit eines der beliebtesten Frontend-Frameworks...</p>' },
  { id: 2, title: 'JS Async-Programmierung erklärt', category: 'JavaScript', date: '2024-05-08',
    content: '<h2>Was ist Async?</h2><p>JS ist single-threaded...</p>' },
]

function PostPage() {
  const { id } = useParams()

  // Artikel anhand der ID finden
  const article = useMemo(() => {
    return articles.find(a => a.id === Number(id))
  }, [id])

  // Artikel nicht gefunden
  if (!article) {
    return (
      <div className="not-found">
        <h2>Artikel nicht gefunden</h2>
        <p>Artikel mit ID{id}wurde nicht gefunden.</p>
        <Link to="/">Zurück zur Startseite</Link>
      </div>
    )
  }

  // Artikel existiert
  return (
    <article className="post-view">
      <span className="category-tag">{article.category}</span>
      <h1>{article.title}</h1>
      <time>{article.date}</time>
      {/* dangerouslySetInnerHTML ist gleichbedeutend mit Vues v-html, seien Sie sich der XSS-Risiken bewusst */}
      <div
        className="content"
        dangerouslySetInnerHTML={{ __html: article.content }}
      />
      <Link to="/" className="back-link">← Zurück zur Startseite</Link>
    </article>
  )
}

export default PostPage

dangerouslySetInnerHTMList die React-Version vonv-html. Der Name ist absichtlich lang, um Sie daran zu erinnern: Verwenden Sie ihn nicht, um von Benutzern generierte Inhalte zu rendern, es besteht ein XSS-Sicherheitsrisiko. Die Inhalte hier werden von den Entwicklern selbst geschrieben, daher ist es sicher.

Styles für PostPage hinzufügen

Beispiel

/* Dateipfad: Detailseiten-Stile an src/App.css anhängen */
.post-view {
  max-width: 720px;
  margin: 0 auto;
}

.category-tag {
  display: inline-block;
  padding: 4px 12px;
  background: #e3f2fd;
  color: #1976d2;
  border-radius: 12px;
  font-size: 13px;
  margin-bottom: 12px;
}

.post-view h1 {
  font-size: 32px;
  margin-bottom: 12px;
  line-height: 1.4;
}

.post-view time {
  display: block;
  color: #999;
  font-size: 14px;
  margin-bottom: 30px;
}

.content {
  line-height: 1.8;
  font-size: 16px;
  color: #333;
}

.content h2 {
  margin: 24px 0 12px;
  font-size: 22px;
}

.content p {
  margin-bottom: 12px;
}

.back-link {
  display: inline-block;
  margin-top: 40px;
  color: #1976d2;
  text-decoration: none;
}

.not-found {
  text-align: center;
  padding: 60px 0;
}

Kapitelzusammenfassung

In diesem Kapitel haben Sie den Kern von React Router v6 kennengelernt: Routentabellenkonfiguration (createBrowserRouter), Link-Navigation, verschachteltes Outlet, dynamische Route /post/:id, useParams zum Lesen von Parametern und useNavigate für programmatische Navigation.

Jetzt hat der Blog ein echtes Multi-Page-Erlebnis — durchsuchen Sie die Liste auf der Startseite, klicken Sie, um die Detailseite aufzurufen und den vollständigen Inhalt zu lesen.

Andere Erweiterungen