Maison > Article > interface Web > Expériences et mises en garde concernant l'immigration Svelte
J'ai récemment mis à jour une application web assez complexe. L'application possède des fonctionnalités telles que l'authentification, Stripe, i18n, le mode sombre/clair, PWA, etc. Au total, elle compte environ 30 pages et composants, avec presque aucun package npm tiers.
Je voudrais souligner ce que j'ai trouvé assez difficile lors de la migration de l'application vers Svelte 5.
Le script d'auto-migration fourni par Svelte peut faire le travail à votre place avec cette commande "one-liner" dans le terminal npx sv migrate svelte-5 (après avoir effectué toutes les mises à jour et installations nécessaires : "@sveltejs/vite -plugin-svelte": "^4.0.0" et "svelte": "^5"). Mais je ne recommande pas cette approche « marteau ».
Allez fichier par fichier, composant par composant avec Ctrl Shift P (Windows/Linux) / Shift Command P (Mac) et utilisez la commande Migrate Component to Svelte 5 Syntax dans la palette de commandes VS Code à la place. Vous aurez ainsi plus de contrôle.
Le script ne peut pas faire de miracles. La mise à niveau des déclarations de variables réactives vers $state() est généralement correcte. Cependant, le script peut avoir du mal à détecter si $: doit être converti en $derived()/$derived.by(() => {}) ou $effect(() => {}).
Alors, devinez quoi ? Avec le script de migration automatique, vous pourriez vous retrouver avec beaucoup de run(() => {}).
Par exemple, imaginez comme un exemple simplifié en utilisant quelque chose comme ceci :
<script> ... let notext = false; $: if (data.completeDoc == 'NoLangVersion') { notext = true; } $: if (data.completeDoc !== 'NoLangVersion') { notext = false; } </script> ... {#if notext} {data.userPrefferedLang.noTextWarning} {:else} ... {/if} ...
Le script d'auto-migration vous donnera ceci :
<script> import { run } from 'svelte/legacy'; ... let notext = $state(false); run(() => { if (data.completeDoc == 'NoLangVersion') { notext = true; } }); run(() => { if (data.completeDoc !== 'NoLangVersion') { notext = false; } }); </script>
avec un joli petit avertissement indiquant que la fonction run est obsolète.
Le meilleur code Svelte 5 serait celui-ci, je suppose :
<script> ... let notext = $derived.by(() => { if (data.completeDoc == 'NoLangVersion') { return true; } if (data.completeDoc !== 'NoLangVersion') { return false; } }); ... </script>
ou si votre code n'est pas vraiment compliqué, même quelque chose comme ça :
<script> ... let notext = $derived( data.completeDoc == 'NoLangVersion' ? true : false ) ... </script>
La raison est que le script ne peut pas transformer facilement le code en $derived.by(() => {}), il aimerait donc utiliser une approche plus sale avec $effect(). Mais $effect() ne s'exécute que côté client, donc le script utilise à la place la fonction d'exécution obsolète.
Nous arrivons maintenant au point le plus important à retenir. Qui est $effect() exécuté uniquement côté client. Donc pas de $effect() sur le serveur, pour le prérendu des pages et le SSR.
$effect() NE FONCTIONNE PAS SUR LE SERVEUR !
Cela devrait être vraiment souligné dans la documentation de Svelte 5.
Regardez ces deux exemples :
<script> let a = 1 let b = 2 $: c = a + b </script> {c} // server responds with c == 3
<script> let a = $state(1) let b = $state(2) let c = $state(0) $effect(() => { c = a + b }) </script> {c} // server responds with c == 0
Ce ne sont pas les mêmes. Cela pose de nombreux défis. Le client devra réévaluer la variable c lors du montage de la page. La page aura un aspect différent lorsqu'elle sera envoyée depuis le serveur et lorsqu'elle sera finalement rendue en DOM sur le client (SSR, SEO, problèmes de scintillement, etc.).
Essayez donc toujours d'utiliser $derived ou $derived.by(() => {}) plutôt que $effect(). Cela vous évitera bien des ennuis.
C'est à peu près la même histoire que lorsqu'on nous a découragé d'utiliser les magasins de SvelteKit et SSR.
Vous pourriez être tenté de remplacer votre onMount() dans SvelteKit par $effect() grâce aux exemples qui ont été donnés lors de l'arrivée de Svelte 5. Pour les raisons déjà évoquées, je déconseille cela pour le moment. onMount est toujours un crochet essentiel du cycle de vie Svelte.
L'autre belle surprise est que Svelte 5 prend soin d'avoir des valeurs de variables cohérentes. Si vous transmettez une variable en tant qu'accessoire à un composant et modifiez cette variable dans le composant ultérieurement, le script tentera de résoudre cette incohérence en utilisant $bindable $prop. Le parent doit être informé afin que l'état de votre application soit cohérent.
Regardez cet exemple :
<script> ... let notext = false; $: if (data.completeDoc == 'NoLangVersion') { notext = true; } $: if (data.completeDoc !== 'NoLangVersion') { notext = false; } </script> ... {#if notext} {data.userPrefferedLang.noTextWarning} {:else} ... {/if} ...
Le script de migration automatique voudra que vous utilisiez un composant avec une valeur liée pour garantir que le parent puisse récupérer la valeur mise à jour :
<script> import { run } from 'svelte/legacy'; ... let notext = $state(false); run(() => { if (data.completeDoc == 'NoLangVersion') { notext = true; } }); run(() => { if (data.completeDoc !== 'NoLangVersion') { notext = false; } }); </script>
Mais peut-être pouvons-nous aussi utiliser une manière plus simple, vous l'aurez deviné, avec $derived() :
<script> ... let notext = $derived.by(() => { if (data.completeDoc == 'NoLangVersion') { return true; } if (data.completeDoc !== 'NoLangVersion') { return false; } }); ... </script>
Une fonctionnalité très intéressante que j'ai trouvée lors de la migration était que nous pouvons maintenant utiliser CSS :global avec block. Le style avec :global est tout à fait nécessaire si vous souhaitez styliser les éléments HTML dans @html, par exemple.
Donc au lieu de ça :
<script> ... let notext = $derived( data.completeDoc == 'NoLangVersion' ? true : false ) ... </script>
vous pouvez utiliser ceci :
<script> let a = 1 let b = 2 $: c = a + b </script> {c} // server responds with c == 3
Dans Svelte 4, si vous vouliez fournir une classe CSS comme accessoire à un composant, vous utiliseriez {$$props.class} :
<script> let a = $state(1) let b = $state(2) let c = $state(0) $effect(() => { c = a + b }) </script> {c} // server responds with c == 0
Dans Svelte 5, vous pouvez utiliser class={className} :
// parent svelte file <script> import ComponentBinded from './ComponentBinded.svelte'; import ComponentWithDerived from './ComponentWithDerived.svelte'; let name = $state('John Wick'); </script> <p>Name value in parent: {name}</p> <ComponentBinded bind:name={name} /> <ComponentWithDerived {name} />
Lorsque j'ai utilisé le script de fusion automatique, j'ai été choqué par la baisse des performances de mon application. Avec Svelte 4, j'avais presque tous les 100 %. Ce n'est qu'après avoir migré manuellement et soigneusement réfléchi à la manière (principalement comment éviter $effect() si possible) que mes scores Lighthouse étaient de nouveau dans le vert.
La migration vers Svelte 5 a pris plus de temps que prévu. Cependant, je n'ai toujours pas poussé cette nouvelle version en production. Les mises à jour de Svelte 5 arrivent toujours à une fréquence assez élevée.
J'espère que mon expérience pourra être utile à d'autres.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!