Shallow routing
Lorsque vous naviguez sur une application SvelteKit, vous créez des entrées d’historique. Des
clics sur les boutons “Précédent” et “Suivant” vont parcourir cette liste d’entrées, ré-exécutant
tout fonction load
concernées et remplaçant les composants de page lorsque nécessaire.
Parfois, il est pratique de créer des entrées d’historique sans naviguer. Par exemple, vous pourriez vouloir afficher une modale de dialogue que l’utilisateur ou l’utilisatrice puisse faire disparaître en appuyant sur le bouton “Précédent” du navigateur. Ceci est particulièrement utile sur les appareils mobiles, où utiliser les mouvements des doigts sont souvent plus naturels qu’interagire directement avec l’interface. Dans ces cas-là, une modale non-associée à une entrée d’historique peut être une source de frustration, puisqu’un utilisateur ou utilisatrice pourrait faire glisser son doigt vers la gauche pour tenter de fermer la modale, pour au final se retrouver sur la mauvaise page.
SvelteKit rend cela possible grâce aux fonctions pushState
et
replaceState
, qui vous permettent d’associer un état avec une
entrée d’historique sans avoir à naviguer. Par exemple, pour implémenter une modale liée à
l’historique :
<script>
import { pushState } from '$app/navigation';
import { page } from '$app/state';
import Modal from './Modal.svelte';
function showModal() {
pushState('', {
showModal: true
});
}
</script>
{#if page.state.showModal}
<Modal close={() => history.back()} />
{/if}
<script lang="ts">
import { pushState } from '$app/navigation';
import { page } from '$app/state';
import Modal from './Modal.svelte';
function showModal() {
pushState('', {
showModal: true
});
}
</script>
{#if page.state.showModal}
<Modal close={() => history.back()} />
{/if}
La modale peut être fermée en naviguant vers l’arrière (en désactivant page.state.showModal
) ou en
interagissant avec elle de sorte à exécuter le callback close
, ce qui déclenchera la navigation
arrière programmatiquement.
API
Le premier argument de pushState
est l’URL, relative à l’URL courante. Pour rester sur l’URL
courante, utilisez ''
.
Le deuxième argument est l’état de la nouvelle page, auquel vous pouvez avoir accès via l’objet de
page en tant que page.state
. Vous pouvez typer l’état de page en déclarant une
interface App.PageState
(en général dans src/app.d.ts
).
Pour définir l’état de page sans créer une nouvelle entrée d’historique, utilisez replaceState
plutôt que pushState
.
Legacy mode
page.state
importé depuis$app/state
a été ajouté dans la version 2.12 de SvelteKit. Si vous utilisez une version antérieure ou si vous utilisez Svelte 4, utilisez plutôt$page.state
importé depuis$app/stores
.
Charger des données pour une route
Lorsque vous utilisez le shallow routing, vous pourriez vouloir afficher une autre +page.svelte
à
l’intérieur de la page courante. Par exemple, on peut imaginer un clic sur une vignette de photo qui
viendrait afficher le détail de la photo sans avoir à naviguer vers la page de la photo.
Pour que cela fonctionne, vous avez besoin de charger les données attendues par +page.svelte
. Une
manière pratique de faire cela est d’utiliser la fonction
preloadData
au sein du gestionnaire de click
d’un élément <a>
.
Si l’élément (ou un parent) utilise
data-sveltekit-preload-data
, les données auront déjà
été demandées, et preloadData
utilisera alors cette requête.
<script>
import { preloadData, pushState, goto } from '$app/navigation';
import { page } from '$app/state';
import Modal from './Modal.svelte';
import PhotoPage from './[id]/+page.svelte';
let { data } = $props();
</script>
{#each data.thumbnails as thumbnail}
<a
href="/photos/{thumbnail.id}"
onclick={async (e) => {
if (innerWidth < 640 // ignore si l'écran est trop petit
|| e.shiftKey // ou si le lien est ouvert dans une autre fenêtre
|| e.metaKey || e.ctrlKey // ou un nouvel onglet (mac: metaKey, win/linux: ctrlKey)
// on devrait en théorie également considérer un clic avec le bouton de défilement de la
// souris
) return;
// empêche la navigation
e.preventDefault();
const { href } = e.currentTarget;
// ré-exécute les fonctions `load` (ou plutôt, récupère le résultat des fonctions `load` qui
// sont déjà en cours à cause de `data-sveltekit-preload-data`)
const result = await preloadData(href);
if (result.type === 'loaded' && result.status === 200) {
pushState(href, { selected: result.data });
} else {
// quelque chose s'est mal passé ! on essaie de naviguer
goto(href);
}
}}
>
<img alt={thumbnail.alt} src={thumbnail.src} />
</a>
{/each}
{#if page.state.selected}
<Modal onclose={() => history.back()}>
<!-- fournit les données de page au composant +page.svelte
comme le ferait SvelteKit lors d'une navigation -->
<PhotoPage data={page.state.selected} />
</Modal>
{/if}
<script lang="ts">
import { preloadData, pushState, goto } from '$app/navigation';
import { page } from '$app/state';
import Modal from './Modal.svelte';
import PhotoPage from './[id]/+page.svelte';
let { data } = $props();
</script>
{#each data.thumbnails as thumbnail}
<a
href="/photos/{thumbnail.id}"
onclick={async (e) => {
if (innerWidth < 640 // ignore si l'écran est trop petit
|| e.shiftKey // ou si le lien est ouvert dans une autre fenêtre
|| e.metaKey || e.ctrlKey // ou un nouvel onglet (mac: metaKey, win/linux: ctrlKey)
// on devrait en théorie également considérer un clic avec le bouton de défilement de la
// souris
) return;
// empêche la navigation
e.preventDefault();
const { href } = e.currentTarget;
// ré-exécute les fonctions `load` (ou plutôt, récupère le résultat des fonctions `load` qui
// sont déjà en cours à cause de `data-sveltekit-preload-data`)
const result = await preloadData(href);
if (result.type === 'loaded' && result.status === 200) {
pushState(href, { selected: result.data });
} else {
// quelque chose s'est mal passé ! on essaie de naviguer
goto(href);
}
}}
>
<img alt={thumbnail.alt} src={thumbnail.src} />
</a>
{/each}
{#if page.state.selected}
<Modal onclose={() => history.back()}>
<!-- fournit les données de page au composant +page.svelte
comme le ferait SvelteKit lors d'une navigation -->
<PhotoPage data={page.state.selected} />
</Modal>
{/if}
Inconvénients
Lors du rendu côté serveur,page.state
est toujours un objet vide. C’est également le cas pour la
première page sur laquelle l’utilisateur ou l’utilisatrice arrive — si cette personne recharge la
page (ou revient depuis un autre site), l’état ne sera pas appliqué avant la navigation suivante.
During server-side rendering, page.state
is always an empty object. The same is true for the fir
Le shallow routing est une fonctionnalité qui nécessite JavaScript pour fonctionner. Soyez-en conscient•e lorsque vous vous en servez et essayez de penser à un comportement de remplacement dans les cas où JavaScript ne serait pas disponible.