Skip to main content

Données hydratables

En Svelte, lorsque vous souhaitez rendre du contenu lié à des données asynchrones sur le serveur, vous pouvez simplement les await. C’est génial ! Il y a cependant un écueil : lors de l’hydratation côté client, Svelte doit refaire le travail asynchrone, ce qui bloque l’hydratation d’autant de temps :

<script>
  import { getUser } from 'my-database-library';

	// Ceci va récupérer l'utilisateur sur le serveur, rendre son nom dans le h1,
	// puis, lors de l'hydratation sur le client, récupérer _de nouveau_ l'utilisateur,
	// bloquant l'hydratation jusqu'à ce qu'il ait terminé.
  const user = await getUser();
</script>

<h1>{user.name}</h1>

C’est idiot. Si nous avons déjà fait ce travail coûteux de récupérer les données sur le serveur, nous ne voulons pas le refaire lors de l’hydratation côté client. hydratable est une API bas niveau construite pour régler ce problème. Vous n’en aurez probablement pas besoin souvent — elle sera utilisée sous le capot par n’importe quelle librairie de récupération de données que vous utiliserez. Par exemple, elle utilisée par les fonctions distantes de SvelteKit.

Pour corriger l’exemple ci-dessus :

<script>
  import { hydratable } from 'svelte';
  import { getUser } from 'my-database-library';

	// Lors du rendu côté serveur, ceci va sérialiser et stocker le résultat de `getUser`, l'associant
	// à la clé fournie et l'intégrant dans le contenu `head`. Lors de l'hydratation, Svelte va
	// chercher la version sérialisée, la renvoyant simplement plutôt que d'exécuter `getUser`. Après
	// hydratation, si `hydratable` est de nouveau appelée de nouveau, elle va simplement invoquer
	// `getUser`.
  const user = await hydratable('user', () => getUser());
</script>

<h1>{user.name}</h1>

Cette API peut également être utilisée pour fournir un accès à des valeurs aléatoires ou basées sur le temps que l’on souhaite stables entre le rendu serveur et l’hydratation. Par exemple. pour obtenir un nombre aléatoire qui ne change pas lors de l’hydratation :

import { function hydratable<T>(key: string, fn: () => T): Thydratable } from 'svelte';
const const rand: numberrand = hydratable<number>(key: string, fn: () => number): numberhydratable('random', () => var Math: Math

An intrinsic object that provides basic mathematics functionality and constants.

Math
.Math.random(): number

Returns a pseudorandom number between 0 and 1.

random
());

Si vous êtes un•e auteur ou autrice de librairie, assurez-vous de préfixer les clés de vos valeurs hydratables avec le nom de votre librairie afin que les clés ne rentrent pas en conflit avec d’autres librairies.

Sérialisation

Toutes les données renvoyées par une invocation d’hydratable doivent être sérialisables. Mais cela ne signifie pas que vous êtes limité•es au JSON — Svelte utilise devalue, qui peut sérialiser toutes sortes de choses incluant les Map, Set, Url, BigInt. Consultez la documentation pour obtenir la liste complète. En plus, grâce à un peu de magie Svelte, vous pouvez également utiliser des promesses sans crainte :

<script>
  import { hydratable } from 'svelte';
  const promises = hydratable('random', () => {
	return {
	  one: Promise.resolve(1),
	  two: Promise.resolve(2)
	}
  });
</script>

{await promises.one}
{await promises.two}

CSP

hydratable ajoute un bloc <script> inliné au head renvoyé depuis par render. Si vous utilisez les Content Security Policy (CSP), ce script va très certainement échouer à s’exécuter. Vous pouvez fournir la valeur nonce à render :

server
const const nonce: `${string}-${string}-${string}-${string}-${string}`nonce = var crypto: Cryptocrypto.Crypto.randomUUID(): `${string}-${string}-${string}-${string}-${string}`

Available only in secure contexts.

MDN Reference

randomUUID
();
const { const head: string

HTML that goes into the &#x3C;head>

head
, const body: string

HTML that goes somewhere into the &#x3C;body>

body
} = await
render<SvelteComponent<Record<string, any>, any, any>, Record<string, any>>(component: ComponentType<SvelteComponent<Record<string, any>, any, any>>, options?: {
    ...;
} | undefined): RenderOutput

Only available on the server and when compiling with the server option. Takes a component and returns an object with body and head properties on it, which you can use to populate the HTML when server-rendering your app.

render
(const App: LegacyComponentTypeApp, {
csp?: Csp | undefinedcsp: { nonce?: string | undefinednonce } });

Ceci va ajouter la valeur nonce au bloc de script, en supposant que vous allez ajouter la même valeur nonce l’en-tête de CSP du document le contenant :

server
let response: Responseresponse.Response.headers: Headersheaders.Headers.set(name: string, value: string): voidset(
  'Content-Security-Policy',
  `script-src 'nonce-${let nonce: stringnonce}'`
 );

Il est essentiel qu’une valeur nonce — qui, en dehors de sa signification en argot britannique, veut dire ‘nombre utilisé une seule fois’ (number used once) — ne soit utilisée que lors du rendu dynamique côté serveur d’une réponse individuelle.

Si à la place vous générez du HTML statique en avance de phase, vous devez plutôt utiliser des hashes :

server
const { const head: string

HTML that goes into the &#x3C;head>

head
, const body: string

HTML that goes somewhere into the &#x3C;body>

body
,
const hashes: {
    script: `sha256-${string}`[];
}
hashes
} = await
render<SvelteComponent<Record<string, any>, any, any>, Record<string, any>>(component: ComponentType<SvelteComponent<Record<string, any>, any, any>>, options?: {
    ...;
} | undefined): RenderOutput

Only available on the server and when compiling with the server option. Takes a component and returns an object with body and head properties on it, which you can use to populate the HTML when server-rendering your app.

render
(const App: LegacyComponentTypeApp, {
csp?: Csp | undefinedcsp: { hash?: boolean | undefinedhash: true } });

hashes.script sera un tableau de chaînes de caractères comme ["sha256-abcd123"]. Comme avec nonce, les hashes doivent être fournis dans vos en-têtes CSP :

server
let response: Responseresponse.Response.headers: Headersheaders.Headers.set(name: string, value: string): voidset(
  'Content-Security-Policy',
  `script-src ${
let hashes: {
    script: string[];
}
hashes
.script: string[]script.Array<string>.map<string>(callbackfn: (value: string, index: number, array: string[]) => string, thisArg?: any): string[]

Calls a defined callback function on each element of an array, and returns an array that contains the results.

@paramcallbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.
@paramthisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
map
((hash: stringhash) => `'${hash: stringhash}'`).Array<string>.join(separator?: string): string

Adds all the elements of an array into a string, separated by the specified separator string.

@paramseparator A string used to separate one element of the array from the next in the resulting string. If omitted, the array elements are separated with a comma.
join
(' ')}`
);

Nous recommandons l’usage de nonce si possible plutôt que les hashes, puisque hash va interférer dans le futur avec le streaming du SSR.

Modifier cette page sur Github llms.txt