SvelteKit ist das Neueste dessen, was ich als Anwendungs-Frameworks der nächsten Generation bezeichnen würde. Es erstellt natürlich eine Anwendung für Sie, mit dem dateibasierten Routing, der Bereitstellung und dem serverseitigen Rendering, das Next schon immer geboten hat. Aber SvelteKit unterstützt auch verschachtelte Layouts, Server-Mutationen, die Daten auf Ihrer Seite synchronisieren, und einige andere Annehmlichkeiten, auf die wir noch eingehen werden.
Dieser Beitrag soll eine Einführung auf hohem Niveau sein, um hoffentlich Begeisterung bei jedem zu wecken, der SvelteKit noch nie verwendet hat. Es wird eine entspannte Tour. Wenn Ihnen gefällt, was Sie sehen, finden Sie hier die vollständige Dokumentation.
In gewisser Weise ist dieser Beitrag schwierig zu schreiben. SvelteKit ist ein Anwendungs-Framework. Es existiert, um Ihnen beim Erstellen von... nun ja, Anwendungen zu helfen. Das macht es schwer zu demonstrieren. Es ist nicht praktikabel, eine ganze Anwendung in einem Blogbeitrag zu erstellen. Stattdessen werden wir unserer Fantasie ein wenig freien Lauf lassen. Wir werden das Grundgerüst einer Anwendung erstellen, einige leere UI-Platzhalter und fest kodierte statische Daten haben. Das Ziel ist nicht, eine tatsächliche Anwendung zu erstellen, sondern Ihnen zu zeigen, wie die sich bewegenden Teile von SvelteKit funktionieren, damit Sie Ihre eigene Anwendung erstellen können.
Zu diesem Zweck werden wir die bewährte To-Do-Anwendung als Beispiel erstellen. Aber keine Sorge, es wird viel, viel mehr darum gehen, zu sehen, wie SvelteKit funktioniert, als noch eine To-Do-App zu erstellen.
Der Code für alles in diesem Beitrag ist auf GitHub verfügbar. Dieses Projekt ist auch auf Vercel bereitgestellt für eine Live-Demo.
Erstellen Ihres Projekts
Das Aufsetzen eines neuen SvelteKit-Projekts ist einfach genug. Führen Sie npm create svelte@latest Ihr-app-name im Terminal aus und beantworten Sie die Fragen. Wählen Sie unbedingt „Skeleton Project“, aber ansonsten treffen Sie die gewünschten Auswahlen für TypeScript, ESLint usw.
Sobald das Projekt erstellt ist, führen Sie npm i und npm run dev aus und ein Entwicklungsserver sollte gestartet werden. Rufen Sie localhost:5173 im Browser auf und Sie erhalten die Platzhalterseite für die Skeleton-App.
Grundlegendes Routing
Beachten Sie den Ordner routes unter src. Dort befindet sich der Code für alle unsere Routen. Es gibt bereits eine Datei +page.svelte darin mit Inhalt für die Stammroute /. Unabhängig davon, wo Sie sich in der Dateihierarchie befinden, hat die tatsächliche Seite für diesen Pfad immer den Namen +page.svelte. In diesem Sinne erstellen wir Seiten für /list, /details, /admin/user-settings und admin/paid-status und fügen für jede Seite auch einige Textplatzhalter hinzu.
Ihre Dateistruktur sollte etwa so aussehen

Sie sollten in der Lage sein, zu navigieren, indem Sie die URL-Pfade in der Adressleiste des Browsers ändern.

Layouts
Wir wollen Navigationslinks in unserer App, aber wir wollen auf keinen Fall die Markup-Struktur auf jeder Seite kopieren, die wir erstellen. Lassen Sie uns also eine Datei +layout.svelte im Stammverzeichnis unseres routes-Ordners erstellen, die SvelteKit als globales Template für alle Seiten behandelt. Fügen wir ihr etwas Inhalt hinzu
<nav>
<ul>
<li>
<a href="/">Home</a>
</li>
<li>
<a href="/list">To-Do list</a>
</li>
<li>
<a href="/admin/paid-status">Account status</a>
</li>
<li>
<a href="/admin/user-settings">User settings</a>
</li>
</ul>
</nav>
<slot />
<style>
nav {
background-color: beige;
}
nav ul {
display: flex;
}
li {
list-style: none;
margin: 15px;
}
a {
text-decoration: none;
color: black;
}
</style>
Ein rudimentärer Navigationsbereich mit einigen grundlegenden Stilen. Von besonderer Bedeutung ist das Tag <slot />. Dies ist nicht der Slot, den Sie mit Webkomponenten und Shadow DOM verwenden, sondern ein Svelte-Feature, das angibt, wo der Inhalt platziert werden soll. Wenn eine Seite gerendert wird, wird der Seiteninhalt dort eingefügt, wo sich der Slot befindet.
Und jetzt haben wir eine Navigation! Wir werden keine Designwettbewerbe gewinnen, aber das versuchen wir auch nicht.

Verschachtelte Layouts
Was, wenn wir wollten, dass alle unsere Admin-Seiten das gerade erstellte normale Layout erben, aber auch Dinge teilen, die allen Admin-Seiten gemeinsam sind (aber nur Admin-Seiten)? Kein Problem, wir fügen eine weitere Datei +layout.svelte in unserem Stammverzeichnis admin hinzu, die von allem darunter geerbt wird. Lassen Sie uns das tun und diesen Inhalt hinzufügen
<div>This is an admin page</div>
<slot />
<style>
div {
padding: 15px;
margin: 10px 0;
background-color: red;
color: white;
}
</style>
Wir fügen ein rotes Banner hinzu, das anzeigt, dass dies eine Admin-Seite ist, und dann, wie zuvor, einen <slot />, der angibt, wo wir unseren Seiteninhalt einfügen möchten.
Unser Stamm-Layout von zuvor wird gerendert. Innerhalb des Stamm-Layouts befindet sich ein <slot />-Tag. Der Inhalt des verschachtelten Layouts gelangt in den <slot /> des Stamm-Layouts. Und schließlich definiert das verschachtelte Layout seinen eigenen <slot />, in den der Seiteninhalt gerendert wird.
Wenn Sie zu den Admin-Seiten navigieren, sollten Sie das neue rote Banner sehen

Definieren unserer Daten
OK, lassen Sie uns tatsächliche Daten rendern — oder zumindest sehen, wie wir tatsächliche Daten rendern können. Es gibt hundert Möglichkeiten, eine Datenbank zu erstellen und zu verbinden. Dieser Beitrag handelt jedoch von SvelteKit, nicht von der Verwaltung von DynamoDB, daher werden wir statische Daten „laden“. Aber wir werden die gesamte Maschinerie zum Lesen und Aktualisieren verwenden, die Sie für echte Daten verwenden würden. Für eine echte Web-App ersetzen Sie die Funktionen, die statische Daten zurückgeben, durch Funktionen, die sich mit der von Ihnen verwendeten Datenbank verbinden und diese abfragen.
Erstellen wir ein sehr einfaches Modul in lib/data/todoData.ts, das statische Daten zusammen mit künstlichen Verzögerungen zurückgibt, um echte Abfragen zu simulieren. Sie werden sehen, dass dieser lib-Ordner anderswo über $lib importiert wird. Dies ist ein SvelteKit-Feature für diesen speziellen Ordner, und Sie können sogar Ihre eigenen Aliase hinzufügen.
let todos = [
{ id: 1, title: "Write SvelteKit intro blog post", assigned: "Adam", tags: [1] },
{ id: 2, title: "Write SvelteKit advanced data loading blog post", assigned: "Adam", tags: [1] },
{ id: 3, title: "Prepare RenderATL talk", assigned: "Adam", tags: [2] },
{ id: 4, title: "Fix all SvelteKit bugs", assigned: "Rich", tags: [3] },
{ id: 5, title: "Edit Adam's blog posts", assigned: "Geoff", tags: [4] },
];
let tags = [
{ id: 1, name: "SvelteKit Content", color: "ded" },
{ id: 2, name: "Conferences", color: "purple" },
{ id: 3, name: "SvelteKit Development", color: "pink" },
{ id: 4, name: "CSS-Tricks Admin", color: "blue" },
];
export const wait = async amount => new Promise(res => setTimeout(res, amount ?? 100));
export async function getTodos() {
await wait();
return todos;
}
export async function getTags() {
await wait();
return tags.reduce((lookup, tag) => {
lookup[tag.id] = tag;
return lookup;
}, {});
}
export async function getTodo(id) {
return todos.find(t => t.id == id);
}
Eine Funktion, die ein flaches Array unserer To-Do-Elemente zurückgibt, eine Lookup-Tabelle unserer Tags und eine Funktion, um ein einzelnes To-Do abzurufen (letztere werden wir auf unserer Detailseite verwenden).
Laden unserer Daten
Wie bringen wir diese Daten in unsere Svelte-Seiten? Es gibt eine Reihe von Möglichkeiten, aber vorerst erstellen wir eine Datei +page.server.js in unserem list-Ordner und fügen diesen Inhalt ein
import { getTodos, getTags } from "$lib/data/todoData";
export function load() {
const todos = getTodos();
const tags = getTags();
return {
todos,
tags,
};
}
Wir haben eine Funktion load() definiert, die die für die Seite benötigten Daten abruft. Beachten Sie, dass wir die Aufrufe unserer asynchronen Funktionen getTodos und getTags nicht mit await versehen. Dies würde einen Wasserfall beim Laden von Daten erzeugen, da wir darauf warten würden, dass unsere To-Do-Elemente eingehen, bevor wir unsere Tags laden. Stattdessen geben wir die rohen Promises von load zurück, und SvelteKit erledigt die notwendige Arbeit, um sie mit await zu verarbeiten.
Wie greifen wir also von unserer Seitenkomponente auf diese Daten zu? SvelteKit stellt eine data-Prop für unsere Komponente mit den Daten bereit. Wir greifen mit einer reaktiven Zuweisung auf unsere To-Do-Elemente und Tags zu.
Unsere Listenseitenkomponente sieht jetzt so aus.
<script>
export let data;
$: ({ todo, tags } = data);
</script>
<table cellspacing="10" cellpadding="10">
<thead>
<tr>
<th>Task</th>
<th>Tags</th>
<th>Assigned</th>
</tr>
</thead>
<tbody>
{#each todos as t}
<tr>
<td>{t.title}</td>
<td>{t.tags.map((id) => tags[id].name).join(', ')}</td>
<td>{t.assigned}</td>
</tr>
{/each}
</tbody>
</table>
<style>
th {
text-align: left;
}
</style>
Und dies sollte unsere To-Do-Elemente rendern!

Layout-Gruppen
Bevor wir uns der Detailseite zuwenden und Daten mutieren, werfen wir einen Blick auf ein wirklich nettes SvelteKit-Feature: Layout-Gruppen. Wir haben bereits verschachtelte Layouts für alle Admin-Seiten gesehen, aber was wäre, wenn wir ein Layout zwischen beliebigen Seiten auf derselben Ebene unseres Dateisystems teilen wollten? Insbesondere, was wäre, wenn wir ein Layout nur zwischen unserer Listen- und unserer Detailseite teilen wollten? Wir haben bereits ein globales Layout auf dieser Ebene. Stattdessen können wir ein neues Verzeichnis erstellen, aber mit einem Namen, der in Klammern steht, wie folgt

Wir haben jetzt eine Layout-Gruppe, die unsere Listen- und Detailseiten abdeckt. Ich habe sie (todo-management) genannt, aber Sie können sie beliebig benennen. Um es klarzustellen, dieser Name hat keinen Einfluss auf die URLs der Seiten innerhalb der Layout-Gruppe. Die URLs bleiben gleich; Layout-Gruppen ermöglichen es Ihnen, gemeinsame Layouts zu Seiten hinzuzufügen, ohne dass diese die Gesamtheit eines Verzeichnisses in routes ausmachen.
Wir *könnten* eine Datei +layout.svelte und ein albernes <div>-Banner hinzufügen, das sagt: „Hey, wir verwalten To-Dos“. Aber lassen Sie uns etwas Interessanteres tun. Layouts können load()-Funktionen definieren, um Daten für alle darunter liegenden Routen bereitzustellen. Lassen Sie uns diese Funktionalität nutzen, um unsere Tags zu laden – da wir unsere Tags auf unserer details-Seite verwenden werden – zusätzlich zur list-Seite, die wir bereits haben.
In Wirklichkeit ist das Erzwingen einer Layout-Gruppe nur zur Bereitstellung eines einzelnen Datenstücks fast sicher nicht lohnenswert; es ist besser, diese Daten in der load()-Funktion für jede Seite zu duplizieren. Aber für diesen Beitrag liefert es uns den benötigten Vorwand, um eine neue SvelteKit-Funktion zu sehen!
Zuerst gehen wir in die Datei +page.server.js unserer list-Seite und entfernen die Tags daraus.
import { getTodos, getTags } from "$lib/data/todoData";
export function load() {
const todos = getTodos();
return {
todos,
};
}
Unsere Listenseite sollte jetzt einen Fehler erzeugen, da kein tags-Objekt vorhanden ist. Lassen Sie uns das beheben, indem wir eine Datei +layout.server.js in unserer Layout-Gruppe hinzufügen und dann eine load()-Funktion definieren, die unsere Tags lädt.
import { getTags } from "$lib/data/todoData";
export function load() {
const tags = getTags();
return {
tags,
};
}
Und so, wie von Geisterhand, rendert unsere Listenseite wieder!
Wir laden Daten von mehreren Stellen
Lassen Sie uns genau erläutern, was hier passiert
- Wir haben eine
load()-Funktion für unsere Layout-Gruppe definiert, die wir in+layout.server.jsplatziert haben. - Dies liefert Daten für alle Seiten, die das Layout bedient – was in diesem Fall unsere Listen- und Detailseiten bedeutet.
- Unsere Listenseite definiert ebenfalls eine
load()-Funktion, die in ihrer Datei+page.server.jsliegt. - SvelteKit erledigt die Schwerstarbeit, nimmt die Ergebnisse dieser Datenquellen, fasst sie zusammen und macht beide in
dataverfügbar.
Unsere Detailseite
Wir werden unsere Detailseite verwenden, um ein To-Do-Element zu bearbeiten. Zuerst fügen wir eine Spalte zur Tabelle auf unserer Listenseite hinzu, die zur Detailseite mit der To-Do-ID im Query-String verlinkt.
<td><a href="/details?id={t.id}">Edit</a></td>
Lassen Sie uns nun unsere Detailseite erstellen. Zuerst fügen wir einen Loader hinzu, um das To-Do-Element zu holen, das wir bearbeiten. Erstellen Sie eine Datei +page.server.js im Verzeichnis /details mit folgendem Inhalt
import { getTodo, updateTodo, wait } from "$lib/data/todoData";
export function load({ url }) {
const id = url.searchParams.get("id");
console.log(id);
const todo = getTodo(id);
return {
todo,
};
}
Unser Loader enthält eine url-Eigenschaft, von der wir Werte aus dem Query-String abrufen können. Dies erleichtert die Suche nach dem To-Do-Element, das wir bearbeiten. Lassen Sie uns dieses To-Do zusammen mit der Funktionalität zur Bearbeitung rendern.
SvelteKit hat wunderbare integrierte Mutationsfähigkeiten, solange Sie Formulare verwenden. Erinnern Sie sich an Formulare? Hier ist unsere Detailseite. Zur besseren Übersichtlichkeit habe ich die Stile weggelassen.
<script>
import { enhance } from "$app/forms";
export let data;
$: ({ todo, tags } = data);
$: currentTags = todo.tags.map(id => tags[id]);
</script>
<form use:enhance method="post" action="?/editTodo">
<input name="id" type="hidden" value="{todo.id}" />
<input name="title" value="{todo.title}" />
<div>
{#each currentTags as tag}
<span style="{`color:" ${tag.color};`}>{tag.name}</span>
{/each}
</div>
<button>Save</button>
</form>
Wir holen die Tags wie zuvor vom Loader unserer Layout-Gruppe und das To-Do-Element vom Loader unserer Seite. Wir holen die tatsächlichen tag-Objekte aus der Liste der Tag-IDs des To-Dos und rendern dann alles. Wir erstellen ein Formular mit einem versteckten Eingabefeld für die ID und einem tatsächlichen Eingabefeld für den Titel. Wir zeigen die Tags an und stellen dann eine Schaltfläche zum Absenden des Formulars bereit.
Wenn Ihnen use:enhance aufgefallen ist, teilt dies SvelteKit einfach mit, progressive Verbesserung und Ajax zum Absenden unseres Formulars zu verwenden. Sie werden dies wahrscheinlich immer verwenden.
Wie speichern wir unsere Bearbeitungen?
Beachten Sie das Attribut action="?/editTodo" auf dem Formular selbst? Dies teilt uns mit, wohin wir unsere bearbeiteten Daten senden möchten. In unserem Fall möchten wir an eine „Action“ namens editTodo senden.
Erstellen wir sie, indem wir das Folgende zur Datei +page.server.js hinzufügen, die wir bereits für Details haben (die derzeit eine load()-Funktion enthält, um unser To-Do abzurufen)
import { redirect } from "@sveltejs/kit";
// ...
export const actions = {
async editTodo({ request }) {
const formData = await request.formData();
const id = formData.get("id");
const newTitle = formData.get("title");
await wait(250);
updateTodo(id, newTitle);
throw redirect(303, "/list");
},
};
Form Actions bieten uns ein request-Objekt, das Zugriff auf unsere formData gewährt, welche wiederum eine get-Methode für unsere verschiedenen Formularfelder hat. Wir haben dieses versteckte Eingabefeld für den ID-Wert hinzugefügt, damit wir es hier abrufen können, um das zu bearbeitende To-Do-Element zu finden. Wir simulieren eine Verzögerung, rufen eine neue Methode updateTodo() auf und leiten den Benutzer dann zurück zur Seite /list. Die Methode updateTodo() aktualisiert lediglich unsere statischen Daten; im echten Leben würden Sie eine Art Update in dem Datenspeicher ausführen, den Sie verwenden.
export async function updateTodo(id, newTitle) {
const todo = todos.find(t => t.id == id);
Object.assign(todo, { title: newTitle });
}
Lassen Sie es uns ausprobieren. Zuerst gehen wir zur Listenseite

Klicken wir nun auf den Bearbeiten-Button für eines der To-Do-Elemente, um die Bearbeitungsseite unter /details aufzurufen.

Wir werden einen neuen Titel hinzufügen

Klicken Sie nun auf Speichern. Das sollte uns zurück zu unserer Seite /list bringen, mit dem neuen To-Do-Titel.

Wie tauchte der neue Titel so auf? Das war automatisch. Sobald wir zur Seite /list weitergeleitet haben, hat SvelteKit automatisch alle unsere Loader neu ausgeführt, so wie es es ohnehin getan hätte. Dies ist der Schlüsselvorteil, den Frameworks für Anwendungen der nächsten Generation wie SvelteKit, Remix und Next 13 bieten. Anstatt Ihnen eine bequeme Möglichkeit zum Rendern von Seiten zu geben und Ihnen dann viel Glück beim Abrufen der Endpunkte zu wünschen, die Sie möglicherweise zum Aktualisieren von Daten benötigen, integrieren sie Datenmutationen neben dem Datenladen, sodass die beiden Hand in Hand arbeiten.
Ein paar Dinge, die Sie sich vielleicht fragen…
Diese Mutationsaktualisierung scheint nicht allzu beeindruckend zu sein. Die Loader werden bei jeder Navigation neu ausgeführt. Was wäre, wenn wir keine Weiterleitung in unserer Form-Action hinzugefügt hätten, sondern auf der aktuellen Seite geblieben wären? SvelteKit würde die Aktualisierung in der Form-Action wie zuvor durchführen, aber trotzdem alle Loader für die aktuelle Seite neu ausführen, einschließlich der Loader im Seitenlayout (den Layouts).
Gibt es gezieltere Möglichkeiten, unsere Daten zu invalidieren? Zum Beispiel wurden unsere Tags nicht bearbeitet, daher würden wir sie im echten Leben nicht neu abfragen wollen. Ja, das, was ich Ihnen gezeigt habe, ist nur das Standardverhalten von Formularen in SvelteKit. Sie können das Standardverhalten deaktivieren, indem Sie einen Callback an use:enhance übergeben. Dann stellt SvelteKit manuelle Invalidierungsfunktionen zur Verfügung.
Das Laden von Daten bei jeder Navigation ist potenziell teuer und unnötig. Kann ich diese Daten cachen, so wie ich es mit Tools wie react-query tue? Ja, nur anders. SvelteKit ermöglicht es Ihnen, die Cache-Control-Header, die das Web bereits bereitstellt, festzulegen (und dann zu beachten). Und Cache-Invalidierungsmechanismen werde ich in einem Folgebeitrag behandeln.
Alles, was wir in diesem Artikel getan haben, verwendet statische Daten und modifiziert Werte im Speicher. Wenn Sie alles zurücksetzen und von vorne beginnen müssen, stoppen und starten Sie den npm run dev Node-Prozess neu.
Zusammenfassung
Wir haben kaum an der Oberfläche von SvelteKit gekratzt, aber hoffentlich haben Sie genug gesehen, um begeistert zu sein. Ich kann mich nicht erinnern, wann ich Webentwicklung so lustig fand. Mit Dingen wie Bündelung, Routing, SSR und Bereitstellung, die alle out-of-the-box erledigt werden, kann ich mehr Zeit mit Programmieren als mit Konfigurieren verbringen.
Hier sind noch ein paar weitere Ressourcen, die Sie als nächste Schritte zum Erlernen von SvelteKit verwenden können
- Ankündigung von SvelteKit 1.0 (Svelte Blog)
- Einsteigerkurs SvelteKit (Vercel)
- SvelteKit-Dokumentation
Dies ist der beste Kurs, den ich gefunden habe
https://levelup.video/tutorials/sveltekit
Schöner Artikel!
Ich liebe die Art und Weise, wie sich Svelte entwickelt, nach Jahren der React-Erfahrung ist das eine frische Perspektive auf aktuelle Probleme der Webentwicklung.
Seitenbemerkung: Wenn Sie eine einfache, nicht statische API wünschen, können Sie das json-server-Paket verwenden. Es ist nichts Schwieriges daran, aber es spart Zeit für POCs wie Ihren.
Ich habe das Tutorial noch nicht abgeschlossen, aber es war bisher sehr hilfreich!
Ein paar Stellen, die etwas Klarheit gebrauchen könnten
* Es wäre gut zu erwähnen, dass das Verzeichnis
libfürlib/data/todoData.tsuntersrcliegt.* Beim Rendern der Liste in einer Tabelle hat der Screenshot eine „Edit“-Spalte, die im Code nicht vorhanden ist
* Bei der Erstellung einer Layout-Gruppe sollten Sie erwähnen, dass der Benutzer die Verzeichnisse
ListundDetailsin(todo-management)verschieben sollteVielen Dank für diese Einführung.
Für das Projekt „Assistierte Intelligenz in der Medizin mittels interaktiver Graphen“ benötige ich Hilfe
( 1 ) Die Reaktivität von Svelte 5, (ein vorläufiger Prototyp ist bereits unter https://github.com/aimig-org/neo4j-graph-editor/ vorhanden)
( 2 ) Die Graphdatenbank Neo4j,
( 3 ) Eine visuelle Graphdarstellung wie „vis-network“ basierend auf D3.