Zum Inhalt springen

Aufgabe 08-1a - Mehrseiter mit Next.js

Zu Zen-Modus wechseln
  • Du legst ein Next.js-Projekt an und definierst die Seitenstruktur über Ordner.
  • Du setzt ein gemeinsames Layout mit Navigation, dynamische Segmente und eine eigene 404-Seite um.
  • Du entscheidest begründet zwischen Server- und Client-Komponenten und setzt "use client" so tief wie möglich.
  • Du belegst mit einem Experiment und dem Build-Bericht, wo und wann dein Code läuft.

Der (fiktive) Schul-E-Sport-Verein braucht eine Website: Startseite, Team-Übersicht, eine Detailseite je Team, eine Terminliste und eine Über-uns-Seite. Es ist dein erstes echtes Next.js-Projekt, und es begleitet dich mit der Leitfrage des Frameworks: “Wo läuft mein Code, Server oder Client?” (siehe Kapitel Next.js: Struktur und Routing). Die Stufe Wesentlich_überwiegend passt nach Einheit 8.1 (Projekt, Routing und Layouts), die Stufe Wesentlich_vollständig nach Einheit 8.2 (Server- und Client-Komponenten).

Projektordner anlegen siehe https://qiz.at/projektordner

  • Lies vorher kurz https://qiz.at/assignmentsabgabe durch.

  • Nur vollständige Abgaben (siehe https://qiz.at/rules) können positiv bewertet werden.

  • Abgabe- und Testdetails siehe https://qiz.at/assignmentsabgabe

  • Die Übung liegt als Unterordner uebung-08-1a in deinem Jahres-Repository SJ26_27_4AHITM_SEW_<Benutzername>_exercises. Du legst ihn wie in Einheit 8.1 an und arbeitest allein:

    Terminal-Fenster
    npx create-next-app@latest uebung-08-1a --ts --app --biome --no-tailwind --no-src-dir --no-react-compiler --cache-components --import-alias "@/*" --use-npm --yes
  • Räum die Startseite der Vorlage auf. Alle Teams und Termine sind erfunden; verwende keine echten Namen von Mitschülerinnen und Mitschülern.

  • Schriftliche Antworten stehen in der README.md des Projekts. npm run build muss fehlerfrei durchlaufen, npm run lint ohne Fehler; node_modules/ und .next/ kommen nicht ins Repository.

  • Wurzel-Layout mit Kopfzeile, Navigation und Fußzeile; die Navigation verwendet Link und erscheint auf jeder Seite.
  • Routen /, /teams, /termine und /ueber-uns, jede Seite mit eigener Überschrift und kurzem Inhalt.
  • Die Daten liegen als typisierte Arrays in lib/data.ts, nicht in den Komponenten: interface Team (id als Kurzname für die URL, Name, Spiel, Mitglieder, Spiele, Erfolge) und interface ClubEvent (Titel, Datum als ISO-String, isHomeMatch), mindestens drei Teams und fünf Termine.
  • /teams listet alle Teams als verlinkte Karten; /teams/[id] zeigt die Details eines Teams, die id kommt aus params.
  • Während die Detailseite lädt, zeigt eine loading.tsx im Ordner [id] einen Platzhalter.
  • Unbekannte IDs führen über notFound() auf eine selbst gestaltete app/not-found.tsx.

Tipp: params ist ein Promise: const { id } = await params; in einer async-Seite. Importe aus lib/ schreibst du mit dem Alias als @/lib/data. Nach if (!team) { notFound(); } weiß TypeScript, dass team danach existiert.

Vollständig, wenn:

  • npm run build und npm run lint fehlerfrei durchlaufen.
  • alle vier Routen und die Detailseite erreichbar sind und die Navigation auf jeder Seite mit Link arbeitet.
  • die Daten typisiert in lib/data.ts liegen und keine Komponente eigene Daten-Arrays enthält.
  • loading.tsx vorhanden ist und eine unbekannte ID die eigene 404-Seite zeigt.

Wie unter Allgemein.

Für alle, die in der Einheit mit den Schritten oben fertig sind bzw. optional (= freiwillig), wenn du in der Einheit nicht fertig wirst.

Erweitere (siehe Erweitern in https://qiz.at/rules) den vollständigen Teil der vorherigen Stufe dieses Projekts (ändere oder lösche nur, was für diese Aufgabe unbedingt notwendig ist):

  • Die Terminliste bekommt einen umschaltbaren Filter “nur Heimtermine”. Zerleg nach dem Muster aus Einheit 8.2: Die Seite bleibt Server-Komponente und übergibt die Daten; die Filterkomponente ist Client-Komponente mit useState.
  • Hebe in der Navigation den aktiven Link hervor, mit dem Hook usePathname aus next/navigation. Begründe in der README in einem Satz, warum das eine Client-Komponente sein muss.

Tipp: Die Navigation wandert dafür aus dem Layout in eine eigene Datei mit "use client"; das Layout selbst bleibt Server-Komponente. Weil der Pfad erst bei der Anfrage feststeht, verlangt der Build eine <Suspense>-Grenze um die Navigation; als fallback eignen sich dieselben Links ohne Hervorhebung. Bei Checkboxen liest du event.target.checked.

Vollständig, wenn:

  • der Filter funktioniert, die Seite /termine ohne "use client" auskommt und nur die Filterkomponente die Direktive trägt.
  • der aktive Link hervorgehoben ist, die Navigation eine eigene Client-Komponente ist und die Begründung stimmt.

Wie unter Allgemein.

Für alle, die in der Einheit mit den Schritten oben fertig sind bzw. optional (= freiwillig), wenn du in der Einheit nicht fertig wirst.

Erweitere (siehe Erweitern in https://qiz.at/rules) den vollständigen Teil der vorherigen Stufe dieses Projekts (ändere oder lösche nur, was für diese Aufgabe unbedingt notwendig ist):

  • Erklär in vier bis sechs Sätzen in der README, warum die Teamliste ohne "use client" auskommt und der Terminfilter nicht. Nenn das Entscheidungskriterium allgemein.
  • Experiment: Setz in die Seite /teams (Server) und in die Filterkomponente (Client) je ein console.log("rendering <Name>"). Dokumentier mit einer Tabelle und zwei Screenshots, wo die Ausgaben erscheinen (Terminal oder Browser-Konsole), einmal beim direkten Laden der Seite und einmal beim Navigieren über die Navigation.

Tipp: Nicht jede Zeile in der Browser-Konsole stammt aus dem Browser; achte auf die Markierung “Server”. Und überleg, warum eine Client-Komponente auch im Terminal auftaucht.

Vollständig, wenn:

  • die Begründung das allgemeine Kriterium (Hooks, Events, Browser-APIs) nennt.
  • die Tabelle beide Fälle (direkt laden, navigieren) für beide Komponenten richtig wiedergibt und die Screenshots sie belegen.

Wie unter Allgemein, dazu die Screenshots im Ordner screenshots/.

Für alle, die in der Einheit mit den Schritten oben fertig sind bzw. optional (= freiwillig), wenn du in der Einheit nicht fertig wirst.

Erweitere (siehe Erweitern in https://qiz.at/rules) den vollständigen Teil der vorherigen Stufe dieses Projekts (ändere oder lösche nur, was für diese Aufgabe unbedingt notwendig ist):

  • Gegenprobe: Setz testweise "use client" auf die Seite /teams und beobachte, dass alles weiter funktioniert. Erklär in drei Sätzen, warum es trotzdem die schlechtere Lösung ist, und nimm die Direktive wieder heraus.
  • Lies den Build-Bericht von npm run build: Welche Routen sind ○, welche ◐, und warum? Braucht eine deiner Seiten "use cache" oder eine weitere <Suspense>-Grenze? Begründe.
  • Ergänz ein verschachteltes Layout nur für den Bereich /teams (etwa eine Unternavigation mit allen Teams) und erklär, wie sich Wurzel- und Bereichs-Layout schachteln.

Tipp: Stichworte für die Gegenprobe: ansteckende Direktive, Client-Bundle, Datenzugriff in Kapitel 9. Für den Build-Bericht hilft die Frage, was Next.js zur Bauzeit noch nicht wissen kann.

Vollständig, wenn:

  • die Gegenprobe durchgeführt, die Direktive wieder entfernt und die Erklärung stichhaltig ist.
  • die Einteilung in ○ und ◐ stimmt und die Antwort zu "use cache" und <Suspense> begründet ist.
  • das Bereichs-Layout nur unter /teams erscheint und die Schachtelung richtig erklärt ist.

Wie unter Allgemein.

  1. Wie entsteht in Next.js aus einem Ordner eine Route, und was macht page.tsx darin?
  2. Was leistet Link gegenüber einem gewöhnlichen <a>?
  3. Welche Fähigkeiten haben nur Client-Komponenten, welche nur Server-Komponenten?
  4. Warum gehört "use client" so tief wie möglich in den Komponentenbaum?
  5. Woher weiß eine Seite unter /teams/[id], welches Team gemeint ist?

Durchschnittlich bis Wesentlich_vollständig: ca. 100 min

  • Oliver Maurhart (MAO)
  • Michael Gabriel (GAM)

CC-BY-SA

https://qiz.at/rules