Concepts principaux
Routing
Éditer cette page sur GithubLe cœur de SvelteKit est un routeur basé sur l'arborescence de fichiers. Les routes de votre application — c'est-à-dire les URLs auxquelles les utilisateurs et utilisatrices ont accès — sont définies par les dossiers de votre projet :
src/routes
est la route racinesrc/routes/about
crée une route/about
src/routes/blog/[slug]
crée une route avec un paramètre,slug
, qui peut être utilisé pour charger des données dynamiquement lorsque quelqu'un demande une page comme/blog/hello-world
Vous pouvez utiliser un autre dossier que
src/routes
en modifiant la configuration du projet.
Chaque dossier de route continent un ou plusieurs fichiers de route, que vous pouvez reconnaître à leur préfixe +
.
+pagepermalink
+page.sveltepermalink
Un composant +page.svelte
définit une page de votre application. Par défaut, les pages sont rendues à la fois sur le serveur (SSR) lors la requête initiale et dans le navigateur (CSR) pour les navigations suivantes.
<h1>Bonjour et bienvenue sur mon site !</h1>
<a href="/about">À propos de mon site</a>
<h1>À propos de ce site</h1>
<p>À FAIRE...</p>
<a href="/">Accueil</a>
<script>
/** @type {import('./$types').PageData} */
export let data;
</script>
<h1>{data.title}</h1>
<div>{@html data.content}</div>
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
<h1>{data.title}</h1>
<div>{@html data.content}</div>
Notez que SvelteKit utilise des éléments
<a>
pour naviguer entre les routes, plutôt qu'un composant<Link>
qui serait spécifique au framework.
+page.jspermalink
Souvent, une page a besoin de charger des données avant qu'elle ne puisse être rendue. Pour cela, il suffit d'ajouter un module +page.js
qui exporte une fonction load
:
ts
import {error } from '@sveltejs/kit';/** @type {import('./$types').PageLoad} */export functionload ({params }) {if (params .slug === 'hello-world') {return {title : 'Bonjour tout le monde !',content : 'Bienvenue sur notre blog. Lorem ipsum dolor sit amet...'};}error (404, 'Introuvable');}
ts
import {error } from '@sveltejs/kit';import type {PageLoad } from './$types';export constload :PageLoad = ({params }) => {if (params .slug === 'hello-world') {return {title : 'Bonjour tout le monde !',content : 'Bienvenue sur notre blog. Lorem ipsum dolor sit amet...',};}error (404, 'Introuvable');};
Cette fonction s'exécute avec +page.svelte
, ce qui signifie qu'elle est exécutée sur le serveur lors du rendu côté serveur et dans le navigateur lors de la navigation côté client. Voir load
pour plus de détails sur l'API.
En plus de load
, +page.js
peut exporter des valeurs qui configurent le comportement de la page :
export const prerender = true
oufalse
ou'auto'
export const ssr = true
oufalse
export const csr = true
oufalse
Vous trouverez plus d'informations sur ces valeurs dans la section options de page.
+page.server.jspermalink
Si votre fonction load
doit impérativement être exécutée sur le serveur — par exemple, si elle a besoin de récupérer des données dans une base de données, ou si elle a besoin d'accéder à des variables d'environnement privées comme des clés d'API — vous pouvez alors renommer +page.js
en +page.server.js
et changer le type PageLoad
en PageServerLoad
.
ts
import {error } from '@sveltejs/kit';/** @type {import('./$types').PageServerLoad} */export async functionload ({params }) {constpost = awaitgetPostFromDatabase (params .slug );if (post ) {returnpost ;}error (404, 'Introuvable');}
ts
import {error } from '@sveltejs/kit';import type {PageServerLoad } from './$types';export constload :PageServerLoad = async ({params }) => {constpost = awaitgetPostFromDatabase (params .slug );if (post ) {returnpost ;}error (404, 'Introuvable');};
Lors de la navigation côté client, SvelteKit va charger cette donnée sur le serveur, ce qui implique que la valeur renvoyée doit être sérialisable avec devalue. Voir load
pour plus de détails sur l'API.
Comme +page.js
, +page.server.js
peut exporter des options de page — prerender
, ssr
et csr
.
Un fichier +page.server.js
peut également exporter des actions. Si load
vous permet de lire des données sur le serveur, les actions
vous permettent d'écrire des données sur le serveur en utilisant l'élément <form>
. Pour savoir comment vous en servir, reportez-vous à la section sur les actions de formulaire.
+errorpermalink
Si une erreur survient durant load
, SvelteKit affiche une page d'erreur par défaut. Vous pouvez personnaliser cette page d'erreur en fonction de la route en ajoutant un fichier +error.svelte
:
<script>
import { page } from '$app/stores';
</script>
<h1>{$page.status}: {$page.error.message}</h1>
<script lang="ts">
import { page } from '$app/stores';
</script>
<h1>{$page.status}: {$page.error.message}</h1>
SvekteKit "remonte l'arborescence de fichiers" à la recherche de la page d'erreur la plus proche — si le fichier de l'exemple ci-dessus n'existe pas, il essaie de trouver src/routes/blog/+error.svelte
puis src/routes/+error.svelte
avant d'afficher la page d'erreur par défaut. Si cette page échoue également (ou si l'erreur survient au sein de la fonction load
du +layout
racine, qui est "au-dessus" du fichier +error
racine), SvelteKit sauve les meubles et rend une page d'erreur statique, que vous pouvez personnaliser en créant un fichier src/error.html
.
Si l'erreur survient dans la fonction load
d'un +layout(.server).js
, le fichier d'erreur le plus proche dans l'arbre est un fichier +error.svelte
au-dessus de ce layout (et non au même niveau).
Si aucune route n'est trouvée (404), src/routes/+error.svelte
est affichée (ou la page d'erreur par défaut, si ce fichier n'existe pas).
+error.svelte
n'est pas utilisée lorsqu'une erreur survient dans une fonctionhandle
ou un gestionnaire de requête+server.js
.
Vous pouvez en apprendre plus sur la gestion des erreurs ici.
+layoutpermalink
Jusqu'ici, nous avons traité les pages comme des composants complètement autonomes — lors de la navigation, le composant +page.svelte
actuel est détruit, et un autre prend sa place.
Mais dans beaucoup d'applications, certains éléments doivent être visibles sur chaque page, comme la navigation principale ou le footer. Plutôt que de tous les répéter dans chaque +page.svelte
, nous pouvons les définir dans des layouts.
+layout.sveltepermalink
Pour créer un layout qui s'applique à chaque page, créez un fichier avec le nom src/routes/+layout.svelte
. Le layout par défaut (celui que SvelteKit utilise si vous ne créez pas le votre) ressemble à ça...
<slot></slot>
... mais nous pouvons y ajouter n'importe quel markup, style ou comportement. La seule contrainte est la présence d'un <slot>
représentant le contenu de la page. Par exemple, ajoutons un barre de navigation :
<nav>
<a href="/">Accueil</a>
<a href="/about">À propos</a>
<a href="/settings">Paramètres</a>
</nav>
<slot></slot>
Si nous créeons des pages pour /
, /about
, et /settings
...
<h1>Accueil</h1>
<h1>À propos</h1>
<h1>Paramètres</h1>
... la barre de navigation sera alors toujours visible, et passer d'une page à l'autre ne remplacera que le <h1>
.
Les layouts peuvent être imbriqués. Supposez que nous ayons plusieurs pages de paramètres – /settings
, /settings/profile
et /settings/notifications
– avec un sous-menu partagé (un exemple est disponible sur github.com/settings).
Nous pouvons créer un layout qui s'applique aux pages en-dessous de /settings
(tout en héritant de la barre de navigation du layout racine) :
<script>
/** @type {import('./$types').LayoutData} */
export let data;
</script>
<h1>Paramètres</h1>
<div class="submenu">
{#each data.sections as section}
<a href="/settings/{section.slug}">{section.title}</a>
{/each}
</div>
<slot></slot>
<script lang="ts">
import type { LayoutData } from './$types';
export let data: LayoutData;
</script>
<h1>Paramètres</h1>
<div class="submenu">
{#each data.sections as section}
<a href="/settings/{section.slug}">{section.title}</a>
{/each}
</div>
<slot></slot>
Par défaut, chaque layout hérite du layout au-dessus de lui. Mais parfois ce n'est pas ce que l'on souhaite — dans ce cas, la section layouts avancés peut vous aider.
+layout.jspermalink
De la même manière que +page.svelte
charge sa donnée de +page.js
, votre composant +layout.svelte
peut charger sa donnée dans la fonction load
du fichier +layout.js
.
ts
/** @type {import('./$types').LayoutLoad} */export functionload () {return {sections : [{slug : 'profile',title : 'Profil' },{slug : 'notifications',title : 'Notification' }]};}
ts
import type {LayoutLoad } from './$types';export constload :LayoutLoad = () => {return {sections : [{slug : 'profile',title : 'Profil' },{slug : 'notifications',title : 'Notification' },],};};
Si un +layout.js
exporte des options de page — prerender
, ssr
et csr
— elles seront appliquées par défaut aux pages enfantes.
La donnée renvoyée d'une fonction load
de layout est aussi accessible dans toutes ses pages enfants :
<script>
/** @type {import('./$types').PageData} */
export let data;
console.log(data.sections); // [{ slug: 'profile', title: 'Profile' }, ...]
</script>
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
console.log(data.sections); // [{ slug: 'profile', title: 'Profile' }, ...]
</script>
Souvent, la donnée de layout reste inchangée lors de la navigation entre pages. SvelteKit ré-exécute intelligemment les fonctions
load
uniquement lorsque nécessaire.
+layout.server.jspermalink
Pour exécuter votre fonction load
de layout sur le serveur, déplacez la dans +layout.server.js
, et changez le type LayoutLoad
en LayoutServerLoad
.
Comme +layout.js
, +layout.server.js
peut exporter des options de page — prerender
, ssr
et csr
.
+serverpermalink
En plus des pages, vous pouvez définir des routes à l'aide de fichiers +server.js
(parfois appelées "routes d'API" ou "endpoint"), qui vous donnent un contrôle complet sur votre réponse. Un fichier +server.js
exporte des fonctions correspondant aux verbes HTTP comme GET
, POST
, PATCH
, PUT
, DELETE
, OPTIONS
, et HEAD
qui acceptent un argument RequestEvent
et renvoie un objet Response
.
Par exemple, nous pouvons créer une route /api/random-number
avec une fonction GET
:
ts
import {error } from '@sveltejs/kit';/** @type {import('./$types').RequestHandler} */export functionGET ({url }) {constmin =Number (url .searchParams .get ('min') ?? '0');constmax =Number (url .searchParams .get ('max') ?? '1');constd =max -min ;if (isNaN (d ) ||d < 0) {error (400, 'min et max doivent être des nombres, et min doit être inférieur à max');}constrandom =min +Math .random () *d ;return newResponse (String (random ));}
ts
import {error } from '@sveltejs/kit';import type {RequestHandler } from './$types';export constGET :RequestHandler = ({url }) => {constmin =Number (url .searchParams .get ('min') ?? '0');constmax =Number (url .searchParams .get ('max') ?? '1');constd =max -min ;if (isNaN (d ) ||d < 0) {error (400, 'min et max doivent être des nombres, et min doit être inférieur à max');}constrandom =min +Math .random () *d ;return newResponse (String (random ));};
Le premier argument de la Response
peut être un ReadableStream
, rendant possible l'envoi d'une grande quantité de données sous forme de flux, ou la création d'évènements-serveur (sauf si vous déployez sur des plateformes qui envoient les réponses sous forme de Buffer, comme AWS Lambda).
Vous pouvez utiliser les méthodes error
, redirect
et json
de @sveltejs/kit
pour vous simplifier la vie (mais vous n'en avez pas l'obligation).
Si une erreur survient (soit via error(...)
, soit une erreur inattendue), la réponse sera la représentation JSON de l'erreur ou une page d'erreur de secours — qui peut être personnalisée via src/error.html
— en fonction du header Accept
. Le composant +error.svelte
ne sera pas rendu dans ce cas. Vous pouvez en apprendre plus sur la gestion d'erreurs ici.
Lors que vous créez une fonction
OPTIONS
, notez que Vite injectera les headersAccess-Control-Allow-Origin
etAccess-Control-Allow-Methods
— ceux-ci ne sont pas présents en production à moins que vous ne les ajoutiez.
Recevoir des donnéespermalink
En exportant des fonctions POST
/PUT
/PATCH
/DELETE
/OPTIONS
/HEAD
, les fichiers +server.js
peuvent être utilisés pour créer une API complète :
<script>
let a = 0;
let b = 0;
let total = 0;
async function add() {
const response = await fetch('/api/add', {
method: 'POST',
body: JSON.stringify({ a, b }),
headers: {
'content-type': 'application/json'
}
});
total = await response.json();
}
</script>
<input type="number" bind:value={a}> +
<input type="number" bind:value={b}> =
{total}
<button on:click={add}>Calculer</button>
<script lang="ts">
let a = 0;
let b = 0;
let total = 0;
async function add() {
const response = await fetch('/api/add', {
method: 'POST',
body: JSON.stringify({ a, b }),
headers: {
'content-type': 'application/json',
},
});
total = await response.json();
}
</script>
<input type="number" bind:value={a}> +
<input type="number" bind:value={b}> =
{total}
<button on:click={add}>Calculer</button>
ts
import {json } from '@sveltejs/kit';/** @type {import('./$types').RequestHandler} */export async functionPOST ({request }) {const {a ,b } = awaitrequest .json ();returnjson (a +b );}
ts
import {json } from '@sveltejs/kit';import type {RequestHandler } from './$types';export constPOST :RequestHandler = async ({request }) => {const {a ,b } = awaitrequest .json ();returnjson (a +b );};
En général, les actions de formulaire sont une meilleure façon d'envoyer des données du navigateur vers le serveur.
Si une fonction
GET
est exportée, une requêteHEAD
renverra lecontent-length
du body de la réponse deGET
.
Fonction par défautpermalink
Exporter la fonction fallback
permet de gérer toutes les méthodes HTTP non spécifiées, incluant des méthodes comme MOVE
qui n'ont pas d'export dédié dans +server.js
.
ts
import {json ,text } from '@sveltejs/kit';export async functionPOST ({request }) {const {a ,b } = awaitrequest .json ();returnjson (a +b );}// Cette fonction gèrera PUT, PATCH, DELETE, etc./** @type {import('./$types').RequestHandler} */export async functionfallback ({request }) {returntext (`J'ai géré votre requête ${request .method } !`);}
ts
import {json ,text } from '@sveltejs/kit';import type {RequestHandler } from './$types';export async functionPOST ({request }) {const {a ,b } = awaitrequest .json ();returnjson (a +b );}// Cette fonction gèrera PUT, PATCH, DELETE, etc.export constfallback :RequestHandler = async ({request }) => {returntext (`J'ai géré votre requête ${request .method } !`);};
Pour les requêtes
HEAD
, la fonctionGET
est prioritaire sur la fonctionfallback
.
Négociation de contenupermalink
Les fichiers +server.js
peuvent être placés dans le même dossier que les fichiers +page
, permettant à une route d'être à la fois une page ou un endpoint d'API. Pour les différencier, SvelteKit utilise les règles suivantes :
- les requêtes
PUT
/PATCH
/DELETE
/OPTIONS
sont toujours gérées par+server.js
puisqu'elles ne concernent pas les pages - les requêtes
GET
/POST
/HEAD
sont traitées comme des requêtes de page si le headeraccept
priorisetext/html
(en d'autres mots, c'est un navigateur qui demande une page), et sinon gérées par+server.js
. - les réponses aux requêtes
GET
incluent un headerVary: Accept
, de sorte que les proxy et les navigateurs mettent en cache séparement les réponses HTML et JSON.
$typespermalink
Dans les exemples précédents, nous avons importé les types d'un fichier $types.d.ts
. C'est un fichier que SvelteKit crée pour vous dans un dossier caché si vous utilisez TypeScript (ou JavaScript avec les annotations typées JSDoc) pour vous garantir du typage lorsque vous travaillez avec vos fichiers principaux.
Par exemple, annoter export let data
avec PageData
(ou LayoutData
, pour un fichier +layout.svelte
) dit à TypeScript que le type de data
est du type de ce que renvoie la fonction load
:
<script>
/** @type {import('./$types').PageData} */
export let data;
</script>
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
En retour, annoter la fonction load
avec PageLoad
, PageServerLoad
, LayoutLoad
ou LayoutServerLoad
(pour +page.js
, +page.server.js
, +layout.js
et +layout.server.js
respectivement) garantit que les params
et la valeur de retour sont correctement typées.
Si vous utilisez VS Code ou tout IDE qui supporte le protocole language server ainsi que des plugins TypeScript, vous pouvez alors omettre complètement ces types ! L'outillage d'IDE de Svelte ajoutera les bons types à votre place, pour que vous ayez la vérification de types sans avoir à l'écrire vous-même. Cela fonctionne également avec notre outil de ligne de commande svelte-check
.
Vous pouvez en apprendre plus sur l'omission des $types
dans cet article de blog (en anglais).
Autres fichierspermalink
Tout autre fichier présent dans un dossier de route est ignoré par SvelteKit. Cela implique que vous pouvez placer vos composants et utilitaires à côté des routes qui en ont besoin.
Si des composants et utilitaires sont utilisés par plusieurs routes, c'est une bonne idée de les placer dans le dossier $lib
.