Une introduction rapide à Svelte du point de vue d'un développeur angulaire

Svelte est un cadre d'interface utilisateur relativement nouveau développé par Rich Harris , qui est également l'auteur du constructeur Rollup. Très probablement, Svelte semblera complètement différent de ce que vous avez traité auparavant, mais c'est peut-être même bon. Les deux caractéristiques les plus impressionnantes de ce cadre sont la rapidité et la simplicité. Dans cet article, nous nous concentrerons sur le second.



Étant donné que mon expérience de développement principale est liée à Angular, il est naturel que j'essaie d'apprendre Svelte en copiant les approches qui me sont déjà familières. Et c'est exactement de cela que parlera cet article: comment faire les mêmes choses dans Svelte que dans Angular.


Remarque: Malgré le fait que dans certains cas, j'exprimerai ma préférence, l'article n'est pas une comparaison de cadres. Il s'agit d'une introduction rapide et facile à Svelte pour les personnes qui utilisent déjà Angular comme cadre principal.


Spoiler d'avertissement: Svelte est amusant.


Composants


Dans Svelte, chaque composant est associé au fichier où il est écrit. Par exemple, le composant Button sera créé en nommant le fichier Button.svelte . Bien sûr, nous faisons généralement la même chose dans Angular, mais avec nous, c'est juste une convention. (Dans Svelte, le nom du composant à importer peut également ne pas coïncider avec le nom du fichier - note du traducteur)


Les composants Svelte sont à fichier unique et se composent de 3 sections: script , style et modèle qui n'ont pas besoin d'être enveloppés dans une balise spéciale.


Créons un composant très simple qui montre "Hello World".


hello_world


Importer des composants


En général, cela revient à importer un fichier JS, mais avec quelques mises en garde:


  • vous devez spécifier explicitement l' .svelte fichier du composant .svelte
  • les composants sont importés à l'intérieur de la <script>

 <script> import Todo from './Todo.svelte'; </script> <Todo></Todo> 

À partir des extraits ci-dessus, il est évident que le nombre de lignes pour créer un composant dans Svelte est incroyablement petit. Bien sûr, il y a des implicitités et des limites, mais en même temps, tout est assez simple pour s'y habituer rapidement.


Syntaxe de base


Interpolation


Les interpolations dans Svelte sont plus similaires à celles de React que de Vue ou Angular:


 <script> let someFunction = () => {...} </script> <span>{ 3 + 5 }</span> <span>{ someFunction() }</span> <span>{ someFunction() ? 0 : 1 }</span> 

J'ai l'habitude d'utiliser des accolades doubles, donc je suis parfois scellé, mais je n'ai peut-être que ce problème.


Attributs


La transmission d'attributs aux composants est également assez simple. Les guillemets sont facultatifs et toutes les expressions Javascript peuvent être utilisées:


 //Svelte <script> let isFormValid = true; </script> <button disabled={!isFormValid}></button> 

Les événements


La syntaxe des gestionnaires d'événements est: on:={} .


 <script> const onChange = (e) => console.log(e); </script> <input on:input={onChange} /> 

Contrairement à Angular, nous n'avons pas besoin d'utiliser de parenthèses après le nom de la fonction pour l'appeler. Si vous devez passer des arguments au gestionnaire, utilisez simplement la fonction anonyme:


 <input on:input={(e) => onChange(e, 'a')} /> 

Mon avis sur la lisibilité d'un tel code:


  • Nous devons imprimer moins, car nous n'avons pas besoin de guillemets et de crochets - c'est bien quand même.
  • Lisez plus fort. J'ai toujours aimé l'approche angulaire plutôt que React, donc pour moi et Svelte c'est plus difficile à prendre. Mais c'est juste mon habitude et mon opinion est quelque peu biaisée.

Directives structurelles


Contrairement aux directives structurées dans Vue et Angular, Svelte propose une syntaxe spéciale pour les boucles et les branches à l'intérieur des modèles:


 {#if todos.length === 0}    {:else} {#each todos as todo} <Todo {todo} /> {/each} {/if} 

J'aime vraiment ça. Aucun élément HTML supplémentaire n'est nécessaire, et en termes de lisibilité, cela semble incroyable. Malheureusement, le symbole # dans la disposition du clavier britannique de mon Macbook est dans un endroit inaccessible, ce qui affecte négativement mon expérience avec ces structures.


Propriétés d'entrée


La définition des propriétés pouvant être transmises à un composant (analogue à @Input dans Angular) est aussi simple que d'exporter une variable à partir d'un module JS à l'aide du mot-clé export . Au début, cela peut être déroutant - mais écrivons un exemple et voyons à quel point c'est simple:


 <script> export let todo = { name: '', done: false }; </script> <p> { todo.name } { todo.done ? '✓' : '✕' } </p> 

  • Comme vous pouvez le voir, nous avons initialisé la propriété todo avec la valeur: ce sera la valeur de la propriété par défaut, au cas où elle ne serait pas passée du composant parent

Créez maintenant un conteneur pour ce composant, qui lui transmettra des données:


 <script> import Todo from './Todo.svelte'; const todos = [{ name: " Svelte", done: false }, { name: " Vue", done: false }]; </script> {#each todos as todo} <Todo todo={todo}></Todo> {/each} 

Semblable aux champs d'un objet JS normal, todo={todo} peut être raccourci et réécrit comme suit:


 <Todo {todo}></Todo> 

Au début, cela me semblait étrange, mais maintenant je pense que c'est génial.


Propriétés de sortie


Pour implémenter le comportement de la directive @Output , par exemple, lorsque le composant parent @Output des notifications de l'enfant, nous utiliserons la fonction createEventDispatcher disponible dans Svelte.


  • Importez la fonction createEventDispatcher et affectez sa valeur de retour à la variable de dispatch
  • La fonction de dispatch a deux paramètres: le nom de l'événement et les données (qui tomberont dans le champ de detail de l'objet événement)
  • Nous markDone dispatch à l'intérieur de la fonction markDone , qui est appelée par l'événement click ( on:click )

 <script> import { createEventDispatcher } from 'svelte'; export let todo; const dispatch = createEventDispatcher(); function markDone() { dispatch('done', todo.name); } </script> <p> { todo.name } { todo.done ? '✓' : '✕' } <button on:click={markDone}></button> </p> 

Dans le composant parent, vous devez créer un gestionnaire pour l'événement done afin de pouvoir marquer les objets nécessaires dans le tableau des todo .


  • Créer la fonction onDone
  • Nous affectons cette fonction au gestionnaire d'événements appelé dans le composant enfant, comme suit: on:done={onDone}

 <script> import Todo from './Todo.svelte'; let todos = [{ name: " Svelte", done: false }, { name: " Vue", done: false }]; function onDone(event) { const name = event.detail; todos = todos.map((todo) => { return todo.name === name ? {...todo, done: true} : todo; }); } </script> {#each todos as todo} <Todo {todo} on:done={onDone}></Todo> {/each} 

Remarque: pour déclencher la détection d'un changement dans un objet, nous ne modifions pas l'objet lui-même. Au lieu de cela, nous attribuons à la variable todos un nouveau tableau, où l'objet de la tâche souhaitée sera déjà changé en terminé.


Par conséquent, Svelte est considéré comme vraiment réactif : avec l'affectation habituelle d'une valeur à une variable, la partie correspondante de la représentation changera également.


ngModel


Svelte a une syntaxe de bind:<>={} spéciale bind:<>={} pour lier des variables spécifiques aux attributs d'un composant et les synchroniser entre elles.


En d'autres termes, il vous permet d'organiser une liaison de données bidirectionnelle:


 <script> let name = ""; let description = ""; function submit(e) { //    } </script> <form on:submit={submit}> <div> <input placeholder="" bind:value={name} /> </div> <div> <input placeholder="" bind:value={description} /> </div> <button> </button> </form> 

Expressions réactives


Comme nous l'avons vu précédemment, Svelte répond à l'attribution de valeurs aux variables et redessine la vue. Vous pouvez également utiliser des expressions réactives pour répondre aux modifications de la valeur d'une variable et mettre à jour la valeur d'une autre.


Par exemple, créons une variable qui devrait nous montrer que dans le tableau todos toutes les tâches sont marquées comme terminées:


 let allDone = todos.every(({ done }) => done); 

Toutefois, la vue ne sera pas redessinée lorsque le tableau est mis à jour, car la valeur de la variable allDone affectée qu'une seule fois. Nous utiliserons une expression réactive, qui en même temps nous rappellera l'existence de "labels" en Javascript:


 $: allDone = todos.every(({ done }) => done); 

Ça a l'air très exotique. S'il vous semble qu'il y a «trop de magie», je vous rappelle que les tags sont du Javascript valide .


Une petite démo expliquant ce qui précède:
démo


Injection de contenu


Pour incorporer du contenu, des emplacements sont également utilisés qui sont placés au bon endroit à l'intérieur du composant.


Pour afficher simplement le contenu qui a été transféré à l'intérieur de l'élément composant, un élément slot spécial est utilisé:


 // Button.svelte <script> export let type; </script> <button class.type={type}> <slot></slot> </button> // App.svelte <script> import Button from './Button.svelte'; </script> <Button>  </Button> 

Dans ce cas, la ligne "" prendra la place de l'élément <slot></slot> .
Les emplacements nommés devront être nommés:


 // Modal.svelte <div class='modal'> <div class="modal-header"> <slot name="header"></slot> </div> <div class="modal-body"> <slot name="body"></slot> </div> </div> // App.svelte <script> import Modal from './Modal.svelte'; </script> <Modal> <div slot="header">  </div> <div slot="body">  </div> </Modal> 

Crochets de cycle de vie


Svelte propose 4 crochets de cycle de vie qui sont importés du package svelte .


  • onMount - appelé lorsqu'un composant est monté dans le DOM
  • beforeUpdate - appelé avant la mise à jour du composant
  • afterUpdate - appelé après la mise à jour du composant
  • onDestroy - appelé lorsqu'un composant est supprimé du DOM

La fonction onMount prend en paramètre une fonction de rappel qui sera appelée lorsque le composant sera placé dans le DOM. Autrement dit, il est similaire à l' ngOnInit crochet ngOnInit .


Si la fonction de rappel renvoie une autre fonction, elle sera appelée lorsque le composant sera supprimé du DOM.


 <script> import { onMount, beforeUpdate, afterUpdate, onDestroy } from 'svelte'; onMount(() => console.log('', todo)); afterUpdate(() => console.log('', todo)); beforeUpdate(() => console.log('  ', todo)); onDestroy(() => console.log('', todo)); </script> 

Il est important de se rappeler que lors de l'appel à onMount toutes les propriétés qui y sont incluses doivent déjà être initialisées. Autrement dit, dans le fragment ci-dessus, les todo doivent déjà exister.


Gestion de l'État


La gestion de votre état à Svelte est incroyablement simple, et peut-être que cette partie du cadre me sympathise plus que quiconque. Vous pouvez oublier la verbosité du code lorsque vous utilisez Redux. Par exemple, nous allons créer du stockage dans notre application pour le stockage et la gestion des tâches.


Voûtes enregistrables


Vous devez d'abord importer l' writable de stockage writable partir du package svelte/store et lui indiquer la valeur initiale initialState


 import { writable } from 'svelte/store'; const initialState = [{ name: " Svelte", done: false }, { name: " Vue", done: false }]; const todos = writable(initialState); 

Habituellement, je mets du code similaire dans un fichier séparé comme todos.store.js et todos.store.js exporte la variable de stockage afin que le composant où je l'importe puisse fonctionner avec.


De toute évidence, l'objet todos est devenu un référentiel et n'est plus un tableau. Pour obtenir la valeur de stockage, nous allons utiliser un peu de magie dans Svelte:


  • En ajoutant le caractère $ au nom de la variable de stockage, nous obtenons un accès direct à sa valeur!

Ainsi, nous remplaçons simplement dans le code toutes les références à la variable todos par $todos :


 {#each $todos as todo} <Todo todo={todo} on:done={onDone}></Todo> {/each} 

Réglage de l'état


La nouvelle valeur du stockage enregistrable peut être spécifiée en appelant la méthode set , qui change impérativement l'état en fonction de la valeur transmise:


 const todos = writable(initialState); function removeAll() { todos.set([]); } 

Mise à jour du statut


Pour mettre à jour le stockage (dans notre cas, todos ), en fonction de son état actuel, vous devez appeler la méthode de update et lui passer une fonction de rappel qui renverra le nouvel état du stockage.


Nous onDone fonction onDone que nous avons créée précédemment:


 function onDone(event) { const name = event.detail; todos.update((state) => { return state.map((todo) => { return todo.name === name ? {...todo, done: true} : todo; }); }); } 

Ici, j'ai utilisé un réducteur directement dans le composant, ce qui est une mauvaise pratique. Nous le déplaçons dans un fichier avec notre référentiel et en exportons une fonction qui mettra simplement à jour l'état.


 // todos.store.js export function markTodoAsDone(name) { const updateFn = (state) => { return state.map((todo) => { return todo.name === name ? {...todo, done: true} : todo; }); }); todos.update(updateFn); } // App.svelte import { markTodoAsDone } from './todos.store'; function onDone(event) { const name = event.detail; markTodoAsDone(name); } 

Abonnement au changement de statut


Afin de découvrir que la valeur dans le référentiel a changé, vous pouvez utiliser la méthode d' subscribe . Gardez à l'esprit que le référentiel n'est pas un objet observable , mais fournit une interface similaire.


 const subscription = todos.subscribe(console.log); subscription(); //     

Observables


Si cette partie vous a causé le plus d'enthousiasme, alors je m'empresse de plaire qu'il n'y a pas si longtemps, le support RxJS a été ajouté à Svelte et l'Observable pour ECMAScript a été introduit.


En tant que développeur Angular, je suis déjà habitué à travailler avec une programmation réactive, et l'absence d'un équivalent de canal asynchrone serait extrêmement gênant. Mais Svelte m'a aussi surpris ici.


Voyons un exemple de la façon dont ces outils fonctionnent ensemble: afficher une liste de référentiels sur Github, trouvée par le mot-clé "Svelte" .


Vous pouvez copier le code ci-dessous et l'exécuter directement dans le REPL :


 <script> import rx from "https://unpkg.com/rxjs/bundles/rxjs.umd.min.js"; const { pluck, startWith } = rx.operators; const ajax = rx.ajax.ajax; const URL = `https://api.github.com/search/repositories?q=Svelte`; const repos$ = ajax(URL).pipe( pluck("response"), pluck("items"), startWith([]) ); </script> {#each $repos$ as repo} <div> <a href="{repo.url}">{repo.name}</a> </div> {/each} <!--   Angular: <div *ngFor="let repo of (repos$ | async)> <a [attr.href]="{{ repo.url }}">{{ repo.name }}</a> </div> --> 

Ajoutez simplement le symbole $ au nom de la variable observable repos$ et Svelte affichera automatiquement son contenu.


Ma liste de souhaits pour Svelte


Support typographique


En tant que passionné de Typescript, je ne peux m'empêcher de souhaiter la possibilité d'utiliser des types dans Svelte. J'y suis tellement habitué que parfois je m'emballe et arrange des types dans mon code, que je dois ensuite supprimer. J'espère vraiment que Svelte ajoutera bientôt le support pour Typescript. Je pense que cet article sera sur la liste de souhaits de tous ceux qui souhaitent utiliser Svelte avec une expérience avec Angular.


Accords et directives


Le rendu de la représentation de n'importe quelle variable du bloc <script> est une fonctionnalité très puissante du framework, mais à mon avis, cela peut conduire à un encombrement de code. J'espère que la communauté Svelte travaillera à travers un certain nombre de conventions et de directives pour aider les développeurs à écrire du code de composant propre et compréhensible.


Soutien communautaire


Svelte est un projet grandiose qui, avec un effort accru de la communauté dans la rédaction de packages tiers, de manuels, d'articles de blog et plus encore, peut décoller et devenir un outil reconnu dans le monde étonnant du développement Frontend que nous avons aujourd'hui.


En conclusion


Malgré le fait que je n'étais pas fan de la version précédente du framework, Svelte 3 m'a fait bonne impression. C'est simple, petit, mais peut faire beaucoup. Il est si différent de tout ce qui l'entoure qu'il m'a rappelé l'excitation que j'ai ressentie lorsque je suis passé de jQuery à Angular.


Quel que soit le framework que vous utilisez actuellement, l'apprentissage de Svelte ne prendra probablement que quelques heures. Une fois que vous aurez appris les bases et compris les différences avec ce que vous avez l'habitude d'écrire, travailler avec Svelte sera très facile.


Dans la chaîne Telegram en langue russe @sveltejs, vous trouverez sûrement des développeurs qui ont de l'expérience avec divers cadres et sont prêts à partager leurs histoires, leurs pensées et leurs conseils concernant Svelte.

Source: https://habr.com/ru/post/fr467091/


All Articles