Publié par

Il y a 2 mois -

Temps de lecture 10 minutes

J’ai veillé pour vous : Svelte

Vous avez probablement déjà entendu parler de ce nouveau Framework Front-End – encore un ?! – qui commence à se faire un nom et se révèle aussi prometteur que la concurrence, j’ai nommé : Svelte. Si ce n’est pas le cas, il serait grand temps de mettre les pendules à l’heure. Mais rassurez-vous, j’ai veillé pour vous !

Mais en fait, Svelte c’est quoi ?

Svelte, c’est la nouvelle tendance qui se dessine peu à peu dans le paysage des technologies Front-End. Ce dernier s’est notamment fait connaître avec son concept Write less code, qui consiste à écrire moins de code pour un résultat tout aussi performant. D’ailleurs, c’est l’une des promesses de son créateur Rich Harris, éditeur graphique pour le New York Times, aussi auteur de Ractive.js.

 

À l’heure où je vous parle, Svelte en est à sa version 3. Eh oui, depuis sa sortie en 2016, le Framework qui a très peu fait parler de lui rencontre un franc succès et arrive à s’en sortir face aux mastodontes du marché que l’on connaît tous actuellement. La librairie ne compte pas moins de 28 000 étoiles. Voyez par vous-même : GitHub de Svelte.

Oui, mais pourquoi Svelte ?

Alors, vient la question existentielle : « Pourquoi devrais-je m’embêter à apprendre un nouveau Framework alors que d’autres font très bien le job ? ». Comme il a été mentionné précédemment, Svelte part d’une idée toute simple : Write less code… mais pas seulement ! À la différence des Frameworks existants, Svelte compile vos composants en code impératif à l’étape de build, autrement dit en code compréhensible par votre navigateur Web. Ceci permet ainsi de charger un seul bundle.js – et bundle.css accessoirement – dans votre page afin que le navigateur se charge uniquement du rendu de votre application. Ce qui ne nécessite pas de Framework à charger au runtime et encore moins de DOM virtuel, car ce dernier est jugé trop lent1.

Et à l’usage, ça donne quoi ?

Une syntaxe beginner-friendly

Qui dit moins de code, dit moins de boilerplate à maintenir et une syntaxe qui se veut tout aussi simple d’utilisation. Attachez vos ceintures, nous allons voir tout cela ensemble. Si vous n’avez jamais vu un composant écrit avec Svelte, voici un exemple assez basique :

<style>
  .pretty {
    color: purple;
  }
</style>

<script>
  let name = 'World';
</script>

<h1 class="pretty">Hello {name}!</h1>

Svelte, non ? En comparaison avec React, vous auriez probablement dû écrire :

import React from 'react';

class Hello extends React.Component {
  render() {
    const pretty = {
      color: "purple"
    };
    return <div style={pretty}>Hello {this.props.name || "World"}</div>
  }
}

Et avec Angular :

import { Component } from '@angular/core';

@Component({
  selector: 'hello',
  template: '<h1 class="pretty">Hello {{name}}!</h1>',
  styles: [`
    .pretty {
      color: purple;
    }
  `]
})
export class HelloComponent {
  name = 'World';
}

Le résultat est sans appel ! Comptez 125, 207 et 240 caractères respectivement pour Svelte, React et Angular. Comme vous pouvez le voir, avec Svelte : on va à l’essentiel. La définition d’un composant se fait à l’aide des balises familières que sont <style> et <script>. Un composant est purement et simplement, la composition d’HTML, CSS et JavaScript. Notez que tout ce que vous définissez dans la balise <script> est accessible depuis votre template HTML et le CSS contenu dans le composant est encapsulé. Autrement dit, il n’y a aucune chance que vous impactiez d’autres composants. Il est important de préciser que la déclaration de chacun de vos composants doit se faire avec l’extension .svelte.

Gestion des entrées et sorties

Svelte est un Framework orienté composants. Comme chez la plupart de ses pairs, il y a des notions assez récurrentes telles que le fait d’interagir et d’assurer la communication entre composants. Commençons par créer un composant Pokemon.svelte :

<script>
  export let id;
  export let name;
  export let species;
  export let description;
</script>

<div>
  <h1>{name}</h1>
  <p>{description}</p>
    <div><span>National N°: </span>{id}</div>
    <div><span>Species: </span>{species}</div>
</div>

Ici, le mot-clé export sert à indiquer les propriétés à valoriser afin d’interagir avec notre composant. C’est l’unique moyen dont vous disposez pour déclarer les inputs de vos composants. Une fois celui-ci créé, depuis notre App.svelte il vous suffit de l’importer comme ceci :

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

Vous avez sans doute remarqué que la définition de notre composant ne contient aucune notion de nommage et pour cause, le nommage de vos composants se fait uniquement à l’aide du mot-clé import. Ainsi, ils peuvent être utilisés comme des éléments HTML classiques. Passons maintenant à l’étape suivante qui consiste à valoriser les attributs id, name, species et description avec les valeurs souhaitées :

<script>
  import Pokemon from './Pokemon.svelte';
	
  const pokemon = {
    id: 888,
    name: 'Zacian',
    species: 'Warrior Pokémon',
    description: 'Zacian is a Fairy/Steel type Pokémon introduced in Generation 8. It is known as the Warrior Pokémon.'
  };
</script>

<Pokemon id={pokemon.id} 
	 name={pokemon.name}
	 species={pokemon.species}
	 description={pokemon.description}/>

Alternativement, Svelte autorise, comme en React, l’utilisation de l’opérateur de décomposition ou spread operator2 pour passer un ensemble de paramètre en une seule fois :

<Pokemon {...pokemon}/>
Toutefois, cette notation est à prendre avec des pincettes. En abuser reviendrait à diminuer drastiquement la compréhension de votre code. D’autant plus que la décomposition n’est pas conseillée, car difficile à optimiser pour Svelte.

Et les outputs dans tout ça ?! L’abonnement aux évènements provenant du DOM ou de composants se fait à l’aide de la directive on:. Cependant, émettre un évènement implique qu’il a été au préalable défini en utilisant la méthode createEventDispatcher :

<script>
  import { createEventDispatcher } from 'svelte';
	
  export let id;
  export let name;
  export let species;
  export let description;
	
  const dispatch = createEventDispatcher();

  function handleClick() {
    dispatch('selected', {
      id,
      name
    });
  }
</script>

<div on:click={handleClick}>
  <h1>{name}</h1>
  <p>{description}</p>
    <div><span>National N°: </span>{id}</div>
    <div><span>Species: </span>{species}</div>
</div>

La méthode createEventDispatcher qui vous permet de créer des event dispatchers sont des fonctions qui prennent deux arguments : un nom et un payload. Dans notre exemple ci-dessus, l’évènement selected étant créé, il ne reste plus qu’à s’y abonner de la manière suivante depuis notre App.svelte :

<script>
  [...]
	
  function onSelectPokemon(event) {
    alert(`Pokémon N°${event.detail.id} named ${event.detail.name} is selected`);
  }
</script>
  
<Pokemon id={pokemon.id} 
	 name={pokemon.name}
	 species={pokemon.species}
	 description={pokemon.description}
	 on:selected={onSelectPokemon}/>

Il est à noter que le payload transmis implémente l’interface CustomEvent3 de l’API JavaScript. Ainsi lors de son émission, nos données id et name sont accessibles au travers de la propriété detail de notre objet event. Et pour terminer, au clic sur notre composant Pokemon, nous obtenons le résultat suivant :

$: à l’image du destiny operator

Et si maintenant, je vous parle de destiny operator ! Cela vous évoque-t-il quelque chose ? L’exemple suivant devrait vous aider à y voir plus clair :

var a = 10;
var b <= a + 1;
a = 20;
Assert.AreEqual(21, b); 

Toujours pas ? L’exemple ci-dessus pourrait se traduire en JavaScript de la manière suivante :

var a = 10;
var b = a + 1;
a = 20;
b = a + 1;
Assert.AreEqual(21, b); 

Voilà qui devrait être un peu plus clair ! En réalité, le premier exemple illustre le concept destiny operator introduit par Paul Stovell4. Celui-ci stipule qu’une variable n’a pas besoin d’être réaffectée chaque fois que les valeurs dont elles dépendent ont changé. Mais quel est le rapport avec Svelte ? Svelte s’appuie sur ce concept et intègre ce que l’on appelle « la déclaration réactive ». Ainsi avec Svelte, une instruction devient réactive en la préfixant de $: comme ceci :

let a = 10;
$: b = a + 1;
a = 20;
Assert.AreEqual(21, b);

Cependant, l’utilisation du préfixe $: présente quelques limitations, et pour cause : la réactivité d’une instruction n’est déclenchée que par des affectations. Voyons cela d’un peu plus près ! Manipulons, cette fois-ci, un tableau de pokemons :

<script>
  import Pokemon from './Pokemon.svelte';
	
  let pokemons = [
    {
      id: 888,
      name: 'Zacian',
      species: 'Warrior Pokémon',
      description: 'Zacian is a Fairy/Steel type Pokémon introduced in Generation 8. It is known as the Warrior Pokémon.'
    }
  ];

  function addPokemon() {
    const newPokemon = {
      id: 889,
      name: 'Zamazenta',
      species: 'Warrior Pokémon',
      description: 'Zamazenta is a Fighting/Steel type Pokémon introduced in Generation 8. It is known as the Warrior Pokémon.'
    };
    
    pokemons.push(newPokemon);
  }
</script>

<button on:click={addPokemon}>
  Add a Pokémon
</button>

Reprenons notre exemple en déclarant la variable « réactive » pokemonName qui contient une liste de noms et affichons tout ce beau monde :

<script>
  [...]
	
  $: pokemonNames = pokemons.map(pokemon => pokemon.name).join(', ').replace(/, ([^,]*)$/, ' and $1');
</script>

<p>In your Pokédex, you have: {pokemonNames}.</p>

[...]

Au clic du bouton, nous n’auront pas l’effet escompté, et pour cause : les méthodes d’objet Array telles que pop, shift, unshift et splice ne provoquent pas de mise à jour automatique car la référence de l’objet reste inchangée. C’est pour cela qu’il nous faut nécessairement réassigner la variable pokemons à l’ajout de nouveaux éléments :

<script>
  [...]
   
  function addPokemon() {
    [...]
		
    pokemons = pokemons;
  }
</script>

Une syntaxe plus concise reviendrait à utiliser la décomposition et ne faire qu’une seule affectation :

<script>
  [...]
   
  function addPokemon() {
    [...]
    
    pokemons = [
      ...pokemons, 
      newPokemon
    ];
  }
</script>

Et au clic du bouton « Add a Pokémon », nous obtenons le résultat suivant :

Et les performances dans tout ça ?

Une autre promesse qui a été faite par le créateur concerne directement les performances. Qui dit code impératif, dit code bas niveau qui manipule directement le DOM et tout cela sans artifice. La preuve en est, voici le comparatif suivant entre Svelte, Vue.js, React et Angular :

Et donc, au final ?

En conclusion, vous l’aurez compris : Svelte se suffit à lui-même et n’a pas besoin de Framework ou bibliothèque tierce pour fonctionner. Avec la pléthore des technologies existant dans le Front-End, vient la question suivante : « Svelte est-il suffisamment mûr pour débuter un nouveau projet ? » Je vous répondrais que la version 3 du Framework est un indicateur de fiabilité assez significatif et non négligeable. D’autant plus que Svelte dispose d’une excellente communauté : 1975 issues cloturées pour 45 pull request. Cerise sur le gâteau, Svelte est également compatible avec Jest et Storybook, respectivement pour exécuter vos tests unitaires et constituer votre bibliothèque de composants. Il est aussi intéressant de noter l’intégration simple qui est faite avec Webpack afin de faciliter le développement et déploiement de vos applications. Ce qui démontre au final que Svelte suscite beaucoup d’intérêt auprès des utilisateurs. D’ailleurs, le créateur affiche une volonté de supprimer les tâches rébarbatives rencontrées le plus souvent avec d’autres Frameworks, telles qu’initialiser un nouveau projet. De ce fait, le temps d’apprentissage s’en trouve grandement réduit. Je vous conseillerai une chose : essayez par vous-même, vous en ressortirez séduit.

[1]Rich Harris, Virtual DOM is pure overhead, Svelte (2018).
[2]Mozilla Developer Network, Spread syntax, MDN web docs (2020).
[3]Living Standard, Interface CustomEvent, DOM (2020).
[4]Paul Stovell, What is Reactive Programming?, Paulstovell (2010).

Publié par

Commentaire

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Sapient, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.