Concepts principaux
Charger des données
Éditer cette page sur GithubAvant qu'un composant +page.svelte
(et son composant +layout.svelte
qui l'entoure) puisse être rendu, il est souvent nécessaire de récupérer des données. Nous faisons cela avec les fonctions load
.
Données de pagepermalink
Un fichier +page.svelte
peut avoir un fichier partenaire +page.js
qui exporte une fonction load
, dont la valeur de retour est disponible dans la page via la prop data
:
ts
/** @type {import('./$types').PageLoad} */export functionload ({params }) {return {post : {title : `Le titre de ${params .slug } est défini ici`,content : `Le contenu de ${params .slug } est défini ici`}};}
ts
import type {PageLoad } from './$types';export constload :PageLoad = ({params }) => {return {post : {title : `Le titre de ${params .slug } est défini ici`,content : `Le contenu de ${params .slug } est défini ici`,},};};
<script>
/** @type {import('./$types').PageData} */
export let data;
</script>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
Grâce au module généré $types
, nous avons accès aux types automatiquement.
Une fonction load
dans un fichier +page.js
est exécutée à la fois sur le serveur et dans le navigateur (à moins d'être combinée avec export const ssr = false
, ce qui la ferait être uniquement exécutée dans le navigateur). Si votre fonction load
doit toujours être exécuté sur le serveur (parce qu'elle utilise des variables d'environnement privées, ou a besoin d'accéder à une base de donnée par exemple), elle doit alors être définie dans +page.server.js
.
Voici une version plus réaliste de la fonction load
de vos articles de blog, qui est uniquement exécutée sur le serveur et récupère sa donnée dans une base de données :
ts
import * asdb from '$lib/server/database';/** @type {import('./$types').PageServerLoad} */export async functionload ({params }) {return {post : awaitdb .getPost (params .slug )};}
ts
import * asdb from '$lib/server/database';import type {PageServerLoad } from './$types';export constload :PageServerLoad = async ({params }) => {return {post : awaitdb .getPost (params .slug ),};};
Notez que le type a changé de PageLoad
à PageServerLoad
, car les fonctions load
de serveur ont accès à des arguments additionnels. Pour comprendre les cas d'utilisation de +page.js
et ceux de +page.server.js
, lire Universel vs serveur.
Données de layoutpermalink
Vos fichiers +layout.svelte
peuvent aussi charger des données, via +layout.js
ou +layout.server.js
.
ts
import * asdb from '$lib/server/database';/** @type {import('./$types').LayoutServerLoad} */export async functionload () {return {posts : awaitdb .getPostSummaries ()};}
ts
import * asdb from '$lib/server/database';import type {LayoutServerLoad } from './$types';export constload :LayoutServerLoad = async () => {return {posts : awaitdb .getPostSummaries (),};};
<script>
/** @type {import('./$types').LayoutData} */
export let data;
</script>
<main>
<!-- +page.svelte est affichée ici <slot> -->
<slot />
</main>
<aside>
<h2>Plus d'articles</h2>
<ul>
{#each data.posts as post}
<li>
<a href="/blog/{post.slug}">
{post.title}
</a>
</li>
{/each}
</ul>
</aside>
<script lang="ts">
import type { LayoutData } from './$types';
export let data: LayoutData;
</script>
<main>
<!-- +page.svelte est affichée ici <slot> -->
<slot />
</main>
<aside>
<h2>Plus d'articles</h2>
<ul>
{#each data.posts as post}
<li>
<a href="/blog/{post.slug}">
{post.title}
</a>
</li>
{/each}
</ul>
</aside>
Les données renvoyées par les fonctions load
de layout sont accessibles dans le +layout.svelte
auquel il "appartient", le composant +page.svelte
, ainsi que dans les composants +layout.svelte
enfant.
<script>
import { page } from '$app/stores';
/** @type {import('./$types').PageData} */
export let data;
// nous pouvons accéder à `data.posts` car cette donnée est renvoyée
// par la fonction `load` du layout parent
$: index = data.posts.findIndex(post => post.slug === $page.params.slug);
$: next = data.posts[index - 1];
</script>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
{#if next}
<p>Article suivant : <a href="/blog/{next.slug}">{next.title}</a></p>
{/if}
Si plusieurs fonctions
load
renvoient des données portant la même clé, la dernière "gagne" — le résultat d'unload
de layout renvoyant{ a: 1, b: 2 }
et d'unload
de page renvoyant{ b: 3, c: 4 }
serait{ a: 1, b: 3, c: 4 }
$page.datapermalink
Le composant +page.svelte
et tous les composants +layout.svelte
au-dessus ont accès à leurs propres données en plus de toutes les données de leur parents.
Dans certains cas, nous pourrions avoir besoin de l'opposé — un layout parent pourrait avoir besoin d'accéder à la donnée de page ou à la donnée d'un layout enfant. Par exemple, le layout racine pourrait avoir besoin d'accéder à la propriété title
renvoyée par la fonction load
de +page.js
ou +page.server.js
. Ceci est possible grâce à $page.data
:
<script>
import { page } from '$app/stores';
</script>
<svelte:head>
<title>{$page.data.title}</title>
</svelte:head>
<script lang="ts">
import { page } from '$app/stores';
</script>
<svelte:head>
<title>{$page.data.title}</title>
</svelte:head>
Le typage de $page.data
est fourni par App.PageData
.
Universel vs serveurpermalink
Nous avons vu qu'il existe deux types de fonctions load
:
- les fichiers
+page.js
et+layout.js
exportent des fonctionsload
universelles qui s'exécutent à la fois sur le serveur et dans le navigateur - les fichiers
+page.server.js
et+layout.server.js
exportent des fonctionload
de serveur qui s'exécutent uniquement sur le serveur
Conceptuellement, elles font la même chose, mais il existe des différences importantes à connaître.
À quel moment ces fonctions sont exécutées ?permalink
Les fonction load
de serveur sont toujours exécutées sur le serveur.
Par défaut, les fonctions load
universelles s'exécutent sur le serveur pendant le SSR lorsque l'utilisateur ou l'utilisatrice visite la page pour la première fois. Elles sont ensuite de nouveau exécutées lors de l'hydratation, en utilisant les réponses des requêtes fetch
. Toutes les invocations suivantes de fonctions load
universelles ont lieu dans le navigateur. Vous pouvez personnaliser ce comportement avec les options de page. Si vous désactivez le rendu côté serveur, vous obtiendrez un comportement de SPA, et les fonctions load
universelles de la page en question seront toujours exécutées dans le navigateur.
Si une route contient à la fois des fonctions load
universelles et de serveur, la fonction load
de serveur est exécutée en premier.
Une fonction load
est invoquée au moment de l'exécution, sauf en cas de prérendu de la page - dans ce cas, elle est invoquée au moment de la compilation.
Paramètres d'entréepermalink
Les fonctions load
universelles et de serveur ont toutes les deux accès aux propriétés décrivant la requête (params
, route
, et url
) ainsi qu'à diverses fonctions (fetch
, setHeaders
, parent
, depends
et untrack
). Celles-ci sont décrités dans les sections suivantes.
Les fonctions load
de serveur sont appelées avec un évènement ServerLoadEvent
, qui hérite de clientAddress
, cookies
, locals
, platform
, et request
de RequestEvent
.
Les fonctions load
universelles sont appelées avec un évènement LoadEvent
, qui a une propriété data
. Si vous avez des fonctions load
à la fois dans +page.js
et +page.server.js
(ou +layout.js
et +layout.server.js
), la valeur de retour de la fonction load
de serveur est la propriété data
dans l'argument de la fonction load
universelle.
Paramètres de sortiepermalink
Une fonction load
universelle peut renvoyer un objet contenant n'importe quelles valeurs, même des choses comme des classes personnalisée et des constructeurs de composant.
Une fonction load
de serveur doit renvoyer des données pouvant être sérialisées avec devalue — tout ce qui être représenté en JSON plus des choses comme BigInt
, Date
, Map
, Set
et RegExp
, ou des références cycliques — afin qu'elle soit transportée sur le réseau. Votre donnée peut inclure des promesses, dont les valeurs promises seront envoyées en flux aux navigateurs.
Quand utiliser quoipermalink
Les fonctions load
de serveur sont pratiques lorsque vous avez besoin d'accéder à la donnée directement depuis une base de données ou un système de fichiers, ou lorsque vous avez besoin d'utiliser des variables d'environnement privées.
Les fonctions load
universelles sont utiles lorsque vous avez besoin de récupérer avec fetch
des données d'une API externe et n'avez pas besoin d'identifiants privés, puisque SvelteKit peut récupérer la donnée directement depuis l'API plutôt que de faire un aller-retour vers votre serveur. Elles sont également utiles lorsque vous avez besoin de renvoyer quelque chose qui ne peut pas être sérialisé, comme un constructeur de composant Svelte.
Dans de rares cas, vous pourriez avoir besoin d'utiliser les deux ensemble – par exemple, vous pourriez avoir besoin de renvoyer une instance d'une classe personnalisée qui a été initialisée avec des données venant de votre serveur. Lorsque vous utilisez les deux ensemble, la valeur de retour de la fonction load
de serveur n'est pas passée directement à la page, mais à la fonction load
universelle (comme la propriété data
) :
ts
/** @type {import('./$types').PageServerLoad} */export async functionload () {return {serverMessage : 'bonjour depuis la fonction load de serveur'};}
ts
import type {PageServerLoad } from './$types';export constload :PageServerLoad = async () => {return {serverMessage : 'bonjour depuis la fonction load de serveur',};};
ts
/** @type {import('./$types').PageLoad} */export async functionload ({data }) {return {serverMessage :data .serverMessage ,universalMessage : 'bonjour depuis la fonction load universelle'};}
ts
import type {PageLoad } from './$types';export constload :PageLoad = async ({data }) => {return {serverMessage :data .serverMessage ,universalMessage : 'bonjour depuis la fonction load universelle',};};
Utiliser la donnée d'URLpermalink
Souvent, la fonction load
dépend de l'URL d'une manière ou d'une autre. Pour cela, la fonction load
fournit url
, route
et params
.
urlpermalink
Une instance d'URL
, contenant des propriétés telles que origin
, hostname
, pathname
et searchParams
(qui contient la chaîne de caractères de recherche parsée en objet URLSearchParams
). url.hash
ne peut pas être utilisée pendant load
, car elle n'est pas disponible sur le serveur.
Dans certains environnements, cette URL est dérivée des headers de requête pendant le rendu côté serveur. Si vous utilisez l'adaptateur pour Node par exemple, vous pourriez avoir besoin de configurer l'adaptateur pour que l'URL soit correcte.
routepermalink
Contient le nom du dossier de la route actuelle, relativement à src/routes
:
ts
/** @type {import('./$types').PageLoad} */export functionload ({route }) {console .log (route .id ); // '/a/[b]/[...c]'}
ts
import type {PageLoad } from './$types';export constload :PageLoad = ({route }) => {console .log (route .id ); // '/a/[b]/[...c]'};
paramspermalink
params
est dérivé de url.pathname
et route.id
.
Si on suppose une route.id
étant /a/[b]/[...c]
et un url.pathname
étant /a/x/y/z
, l'objet params
ressemblerait à ça :
ts
{"b": "x","c": "y/z"}
Requêter avec fetchpermalink
Pour obtenir des données d'une API externe ou d'une fonction de +server.js
, vous pouvez utiliser la fonction fetch
fournie, qui fonctionne exactement comme l'API web fetch
native avec quelques fonctionnalités supplémentaires :
- Elle peut être utilisée pour faire des requêtes authentifiées sur le serveur, puisqu'elle hérite des headers
cookie
etauthorization
pour la requête de page. - Elle peut faire des requêtes relatives sur le serveur (ordinairement,
fetch
nécessite une URL avec une origine lorsqu'utilisée dans un contexte serveur). - Les requêtes internes (i.e. vers les routes
+server.js
) exécutent directement la fonction concernée lorsqu'émises depuis le serveur, sans appel HTTP supplémentaire. - Pendant le rendu côté serveur, la réponse sera capturée et inlinée dans le HTML construit en utilisant les méthodes
text
,json
etarrayBuffer
de l'objetResponse
. Notez que les headers ne sont pas sérialisés, à moins d'avoir explicitement utiliséfilterSerializedResponseHeaders
. - Pendant l'hydratation, la réponse sera lue depuis le HTML, garantissant la consistance et évitant ainsi une requête réseau additionnelle — si vous recevez un avertissement dans votre console de navigateur après avoir utilisé
fetch
plutôt que lefetch
deload
, c'est la raison.
ts
/** @type {import('./$types').PageLoad} */export async functionload ({fetch ,params }) {constres = awaitfetch (`/api/items/${params .id }`);constitem = awaitres .json ();return {item };}
ts
import type {PageLoad } from './$types';export constload :PageLoad = async ({fetch ,params }) => {constres = awaitfetch (`/api/items/${params .id }`);constitem = awaitres .json ();return {item };};
Cookiespermalink
Une fonction load
de serveur peut récupérer et définir des cookies
.
ts
import * asdb from '$lib/server/database';/** @type {import('./$types').LayoutServerLoad} */export async functionload ({cookies }) {constsessionid =cookies .get ('sessionid');return {user : awaitdb .getUser (sessionid )};}
ts
import * asdb from '$lib/server/database';import type {LayoutServerLoad } from './$types';export constload :LayoutServerLoad = async ({cookies }) => {constsessionid =cookies .get ('sessionid');return {user : awaitdb .getUser (sessionid ),};};
Les cookies seront seulement passés au travers de la fonction fetch
fournie si l'hôte cible est le même que celui de l'application SvelteKit ou un sous-domaine spécifique de celui-ci.
Par exemple, si SvelteKit sert le domaine my.domain.com :
- domain.com NE RECEVRA PAS les cookies
- my.domain.com RECEVRA les cookies
- api.domain.com NE RECEVRA PAS les cookies
- sub.my.domain.com RECEVRA les cookies
Les autres cookies ne seront pas passés lorsque credentials: 'include'
est défini, parce que SvelteKit ne sait pas à quel domaine appartient tel ou tel cookie (le navigateur ne transmet pas cette information), il n'est donc pas sécurisé de les transférer. Vous pouvez contourner ce problème en utilisant le hook handleFetch.
Headerspermalink
Les fonctions load
universelles et de serveur ont toutes les deux accès à une fonction setHeaders
qui, lorsqu'exécutée sur le serveur, peut définir les headers de réponse. (Lorsque exécutée dans le navigateur, setHeaders
n'a pas d'effet.) Cela est utile si vous voulez mettre la page en cache, par exemple :
ts
/** @type {import('./$types').PageLoad} */export async functionload ({fetch ,setHeaders }) {consturl = `https://cms.example.com/products.json`;constresponse = awaitfetch (url );// met la page en cache pour la même durée// que la donnée sous-jacentesetHeaders ({age :response .headers .get ('age'),'cache-control':response .headers .get ('cache-control')});returnresponse .json ();}
ts
import type {PageLoad } from './$types';export constload :PageLoad = async ({fetch ,setHeaders }) => {consturl = `https://cms.example.com/products.json`;constresponse = awaitfetch (url );// met la page en cache pour la même durée// que la donnée sous-jacentesetHeaders ({age :response .headers .get ('age'),'cache-control':response .headers .get ('cache-control'),});returnresponse .json ();};
Définir le même header plusieurs fois (même dans des fonctions load
séparées) est une erreur — vous pouvez uniquement définir un header spécifique une seule fois. Vous ne pouvez pas ajouter un header set-cookie
avec setHeaders
— utilisez plutôt cookies.set(name, value, options)
.
Utiliser la donnée du parentpermalink
Il est occasionnellement utile qu'une fonction load
accède à la donnée d'une fonction load
parent, ce qui est possible avec await parent()
:
ts
/** @type {import('./$types').LayoutLoad} */export functionload () {return {a : 1 };}
ts
import type {LayoutLoad } from './$types';export constload :LayoutLoad = () => {return {a : 1 };};
ts
/** @type {import('./$types').LayoutLoad} */export async functionload ({parent }) {const {a } = awaitparent ();return {b :a + 1 };}
ts
import type {LayoutLoad } from './$types';export constload :LayoutLoad = async ({parent }) => {const {a } = awaitparent ();return {b :a + 1 };};
ts
/** @type {import('./$types').PageLoad} */export async functionload ({parent }) {const {a ,b } = awaitparent ();return {c :a +b };}
ts
import type {PageLoad } from './$types';export constload :PageLoad = async ({parent }) => {const {a ,b } = awaitparent ();return {c :a +b };};
<script>
/** @type {import('./$types').PageData} */
export let data;
</script>
<!-- renders `1 + 2 = 3` -->
<p>{data.a} + {data.b} = {data.c}</p>
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
<!-- renders `1 + 2 = 3` -->
<p>{data.a} + {data.b} = {data.c}</p>
Notez que la fonction
load
de+page.js
reçoit la donnée fusionnée des deux fonctionsload
de layout, pas juste celle du parent immédiat.
Dans +page.server.js
et +layout.server.js
, parent
renvoie la donnée des fichiers +layout.server.js
parents.
Dans +page.js
ou +layout.js
, il renverra la donnée des fichiers +layout.js
parents. En revanche, un fichier +layout.js
manquant est considéré comme une fonction ({ data }) => data
, ce qui implique qu'il renverra aussi la donnée des fichiers +layout.server.js
qui ne sont pas "cachés" par un fichier +layout.js
.
Faites attention à ne pas introduire de cascades de chargement lorsque vous utilisez await parent()
. Ici par exemple, getData(params)
ne dépend pas du résultat de l'appel parent()
, nous devrions donc l'appeler en premier pour éviter de retarder le rendu.
/** @type {import('./$types').PageLoad} */
export async function load({ params, parent }) {
const parentData = await parent();
const data = await getData(params);
const parentData = await parent();
return {
...data
meta: { ...parentData.meta, ...data.meta }
};
}
Erreurspermalink
Si une erreur se produit pendant load
, le composant +error.svelte
le plus proche est rendu. Pour gérer les erreurs attendues, utilisez l'utilitaire error
de @sveltejs/kit
pour préciser le code HTTP ainsi qu'un message optionnel :
ts
import {error } from '@sveltejs/kit';/** @type {import('./$types').LayoutServerLoad} */export functionload ({locals }) {if (!locals .user ) {error (401, 'pas connecté');}if (!locals .user .isAdmin ) {error (403, 'pas administrateur');}}
ts
import {error } from '@sveltejs/kit';import type {LayoutServerLoad } from './$types';export constload :LayoutServerLoad = ({locals }) => {if (!locals .user ) {error (401, 'pas connecté');}if (!locals .user .isAdmin ) {error (403, 'pas administrateur');}};
Appeler error(...)
va lever une exception ; cela permet d'arrêter facilement l'exécution depuis une fonction utilitaire.
Si une erreur inattendue se produit, SvelteKit invoquera la fonction handleError
et traitera l'erreur comme une 500 Internal Error.
Avec SvelteKit 1.x, vous deviez lever une exception avec le retour de la méthode
error
vous-même.
Redirectionspermalink
Pour rediriger les visites, utilisez l'utilitaire redirect
de @sveltejs/kit
pour préciser l'adresse de redirection ainsi qu'un code 3xx
. Comme error(...)
, appeler redirect(...)
va lever une exception, ce qui permet d'arrêter facilement l'exécution depuis une fonction utilitaire.
ts
import {redirect } from '@sveltejs/kit';/** @type {import('./$types').LayoutServerLoad} */export functionload ({locals }) {if (!locals .user ) {redirect (307, '/login');}}
ts
import {redirect } from '@sveltejs/kit';import type {LayoutServerLoad } from './$types';export constload :LayoutServerLoad = ({locals }) => {if (!locals .user ) {redirect (307, '/login');}};
N'utilisez pas
redirect()
à l'intérieur d'un bloctry
-catch
, car la redirection déclenchera le bloccatch
immédiatement.
Dans le navigateur, vous pouvez aussi naviguer programmatiquement en dehors d'une fonction load
en utilisant goto
de $app.navigation
.
Avec SvelteKit 1.x, vous deviez lever une exception avec le retour de la méthode
redirect
vous-même.
Le flux des promessespermalink
Lors de l'exécution d'une fonction load
de serveur, les promesses seront envoyées en flux ("streamées") vers le navigateur à leur résolution. Cela est utile si vous avez des données lourdes et non essentielles, car vous pouvez commencer à afficher la page avant que toute la donnée soit disponible :
ts
/** @type {import('./$types').PageServerLoad} */export async functionload ({params }) {return {// make sure the `await` happens at the end, otherwise we// can't start loading comments until we've loaded the postcomments :loadComments (params .slug ),post : awaitloadPost (params .slug )};}
ts
import type {PageServerLoad } from './$types';export constload :PageServerLoad = async ({params }) => {return {// make sure the `await` happens at the end, otherwise we// can't start loading comments until we've loaded the postcomments :loadComments (params .slug ),post : awaitloadPost (params .slug ),};};
Cela peut servir pour créer des affichages spécifiques de chargement, par exemple :
<script>
/** @type {import('./$types').PageData} */
export let data;
</script>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
{#await data.comments}
Chargement des commentaires...
{:then comments}
{#each comments as comment}
<p>{comment.content}</p>
{/each}
{:catch error}
<p>Erreur lors du chargement des commentaires : {error.message}</p>
{/await}
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
{#await data.comments}
Chargement des commentaires...
{:then comments}
{#each comments as comment}
<p>{comment.content}</p>
{/each}
{:catch error}
<p>Erreur lors du chargement des commentaires : {error.message}</p>
{/await}
Lorsque vous renvoyez des données à travers des promesses, assurez-vous de gérer les exceptions correctement. Plus précisément, le serveur pourrait planter avec une erreur "unhandled promise rejection" si une promesse échoue avant que le rendu ne commence (à ce moment-là, elle est attrapée) et que le serveur ne gère pas l'exception d'une manière ou d'une autre. Si vous utilisez fetch
de SvelteKit directement dans la fonction load
, SvelteKit gérera ce cas pour vous. Pour les autres promesses, il suffit d'attacher un argument de gestion des exceptions catch
vide à la promesse pour la marquer comme gérée :
ts
/** @type {import('./$types').PageServerLoad} */export functionload ({fetch }) {constok_manual =Promise .reject ();ok_manual .catch (() => {});return {ok_manual ,ok_fetch :fetch ('/fetch/that/could/fail'),dangerous_unhandled :Promise .reject ()};}
ts
import type {PageServerLoad } from './$types';export constload :PageServerLoad = ({fetch }) => {constok_manual =Promise .reject ();ok_manual .catch (() => {});return {ok_manual ,ok_fetch :fetch ('/fetch/that/could/fail'),dangerous_unhandled :Promise .reject (),};};
Sur les plateformes qui ne proposent pas le streaming de données, comme AWS Lambda ou Firebase, les réponses seront mises en mémoire tampon. Cela signifie que la page ne sera rendue que lorsque toutes les promesses seront résolues. Si vous utilisez un proxy (i.e. NGINX), vérifiez bien qu'il ne mette pas les réponses de votre serveur en mémoire tampon.
Le streaming de données ne fonctionne que lorsque JavaScript est disponible. Essayez d'éviter de renvoyer des promesses d'une fonction
load
universelle si la page est rendue côté serveur, car celles-ci ne seront pas streamées — à la place, la promesse est recréée lorsque la fonction est réexécutée dans le navigateur.
Les headers et statut d'une réponse ne peuvent pas être changés une fois que la réponse a commencer à être streamée, vous ne pouvez donc pas utiliser
setHeaders
ou faire de redirections à l'intérieur d'une promesse streamée.
Avec SvelteKit 1.x les promesses de premier niveau étaient automatiquement attendues, tandis que les promesses imbriquées étaient asynchrones.
Chargement parallèlepermalink
Lorsque qu'il rend (ou qu'il navigue vers) une page, SvelteKit exécute toutes les fonctions load
en parallèle, en évitant une cascade de requêtes. Pendant la navigation côté client, le résultat des appels de load
serveur multiples sont groupés dans une seule et même réponse. Une fois toutes les fonctions load
terminées, la page est rendue.
Rejouer les fonctions loadpermalink
SvelteKit garde en mémoire les dépendances de chaque fonction load
afin d'éviter de les exécuter inutilement pendant la navigation.
Par exemple, étant donnée une paire de fonctions load
comme celles-ci...
ts
import * asdb from '$lib/server/database';/** @type {import('./$types').PageServerLoad} */export async functionload ({params }) {return {post : awaitdb .getPost (params .slug )};}
ts
import * asdb from '$lib/server/database';import type {PageServerLoad } from './$types';export constload :PageServerLoad = async ({params }) => {return {post : awaitdb .getPost (params .slug ),};};
ts
import * asdb from '$lib/server/database';/** @type {import('./$types').LayoutServerLoad} */export async functionload () {return {posts : awaitdb .getPostSummaries ()};}
ts
import * asdb from '$lib/server/database';import type {LayoutServerLoad } from './$types';export constload :LayoutServerLoad = async () => {return {posts : awaitdb .getPostSummaries (),};};
...celle de +page.server.js
sera réexécutée si nous naviguons de /blog/trying-the-raw-meat-diet
à /blog/i-regret-my-choices
parce que params.slug
a changé. Celle de +layout.server.js
ne sera pas réexécutée, car sa donnée est toujours valide. En d'autres mots, db.getPostSummaries()
ne sera pas appelée une deuxième fois.
Une fonction load
qui appelle await parent()
sera toujours réexécutée si une fonction load
parente est réexécutée.
Le suivi de dépendances ne s'applique pas après que la fonction load
ait fini son exécution — par exemple, accéder à params.x
dans une promesse imbriquée ne déclenchera la réexécution de la fonction lorsque params.x
change. (Ne vous inquiétez pas, vous aurez un avertissement en développement si vous faites ça accidentellement.) Accédez plutôt aux paramètres dans le corps principal de vos fonctions load
.
Les paramètres de recherche sont suivis de manière indépendante du reste de l'URL. Par exemple, faire appel à event.url.searchParams.get("x")
dans une fonction load
aura pour conséquence que cette fonction load
soit réexécutée lors d'une navigation depuis ?x=1
vers ?x=2
, mais pas lors d'une navigation depuis ?x=1&y=1
vers ?x=1&y=2
.
Bloquer le suivi de dépendancespermalink
Dans de rares cas, vous pourriez vouloir exclure quelque chose du mécanisme de suivi de dépendances. Vous pouvez faire cela avec la fonction untrack
:
ts
/** @type {import('./$types').PageLoad} */export async functionload ({untrack ,url }) {// Bloque url.pathname afin qu'un changement de path ne déclenche pas le chargement des donnéesif (untrack (() =>url .pathname === '/')) {return {message : 'Welcome!' };}}
ts
import type {PageLoad } from './$types';export constload :PageLoad = async ({untrack ,url }) => {// Bloque url.pathname afin qu'un changement de path ne déclenche pas le chargement des donnéesif (untrack (() =>url .pathname === '/')) {return {message : 'Welcome!' };}};
Invalidation manuellepermalink
Vous pouvez aussi rejouer les fonctions load
qui s'appliquent à la page actuelle en utilisant invalidate(url)
, qui rejoue toutes les fonctions load
qui dépendent de url
, et invalidateAll()
, qui rejoue toutes les fonctions load
. Les fonctions load
de serveur ne seront jamais automatiquement dépendantes d'une url
requêtée pour éviter de dévoiler des données sensibles au client.
Une fonction load
dépend de url
si elle appelle fetch(url)
ou depends(url)
. Notez que url
peut être un identifiant personnalisé commençant par [a-z]:
:
ts
/** @type {import('./$types').PageLoad} */export async functionload ({fetch ,depends }) {// `load` est rejouée quand `invalidate('https://api.example.com/random-number')` est appelée...constresponse = awaitfetch ('https://api.example.com/random-number');// ...ou quand `invalidate('app:random')` est appeléedepends ('app:random');return {number : awaitresponse .json ()};}
ts
import type {PageLoad } from './$types';export constload :PageLoad = async ({fetch ,depends }) => {// `load` est rejouée quand `invalidate('https://api.example.com/random-number')` est appelée...constresponse = awaitfetch ('https://api.example.com/random-number');// ...ou quand `invalidate('app:random')` est appeléedepends ('app:random');return {number : awaitresponse .json (),};};
<script>
import { invalidate, invalidateAll } from '$app/navigation';
/** @type {import('./$types').PageData} */
export let data;
function rerunLoadFunction() {
// tous ces appels vont rejouer la fonction `load`
invalidate('app:random');
invalidate('https://api.example.com/random-number');
invalidate(url => url.href.includes('random-number'));
invalidateAll();
}
</script>
<p>nombre aléatoire: {data.number}</p>
<button on:click={rerunLoadFunction}>Mettre à jour le nombre</button>
<script lang="ts">
import { invalidate, invalidateAll } from '$app/navigation';
import type { PageData } from './$types';
export let data: PageData;
function rerunLoadFunction() {
// tous ces appels vont rejouer la fonction `load`
invalidate('app:random');
invalidate('https://api.example.com/random-number');
invalidate((url) => url.href.includes('random-number'));
invalidateAll();
}
</script>
<p>nombre aléatoire: {data.number}</p>
<button on:click={rerunLoadFunction}>Mettre à jour le nombre</button>
À quel moment sont rejouées les fonctions load ?permalink
Pour résumer, une fonction load
sera rejouée dans les situations suivantes :
- Elle référence une propriété de
params
dont la valeur a changé - Elle référence une propriété de l'
url
(commeurl.pathname
ouurl.search
) dont la valeur a changé. Les propriétés derequest.url
ne sont pas prises en compte - Elle appelle
url.searchParams.get(...)
,url.searchParams.getAll(...)
ouurl.searchParams.has(...)
et le paramètre en question change. Accéder aux autre propriétés deurl.searchParams
aura le même effet qu'accéder àurl.search
- Elle appelle
await parent()
et une fonctionload
parente a été rejouée - Elle déclare une dépendance à une URL spécifique via
fetch
(fonctions universelles seulement) oudepends
, et cette URL a été déclarée invalide avecinvalidate(url)
- Toutes les fonctions
load
actives ont été rejouées de force avecinvalidateAll()
params
et url
peuvent changer en réponse à un clic sur un lien <a href="..">
, une interaction sur un <form>
, une invocation de goto
, ou un redirect
.
Notez que rejouer une fonction load
va mettre à jour la prop data
dans le composant +layout.svelte
ou +page.svelte
correspondant ; cela n'implique pas la réinstantiation du composant. En conséquence, l'état interne du composant est préservé. Si ce n'est pas le comportement que vous recherchez, vous pouvez réinitialiser ce dont vous avez besoin dans afterNavigate
, et/ou entourer votre composant d'un bloc {#key ...}
.
Implications pour l'authentificationpermalink
Certaines caractéristiques du chargement des données ont des implications importantes pour les contrôles d'authentification :
- Les fonctions
load
des layouts ne sont pas jouées à chaque requête, en particulier lors de navigation côté client entre pages enfants. (Quand est-ce qu'une fonction load est réexécutée ?) - les fonctions
load
des pages et des layouts sont jouées en parallèle à moins queawait parent()
n'ait été appelé. Si un layout lève une exception pendant leload
, la fonctionload
de la page est exécutée mais le client ne recevra pas les données.
Il y a plusieurs stratégies possibles pour s'assurer qu'une vérification soit appelée avec que du code protégé ne soit atteint :
Pour éviter le chargement de données et préserver le client de la mise en cache des fonctions load
des layouts :
- Utiliser hooks pour protéger plusieurs routes avant l'exécution de toute fonction
load
. - Utiliser des fonctions de vérification de droits directement dans les fonctions
+page.server.js
load
pour une protection spécifique de la route.