Javascript

Les frameworks Javascript évanescents : Svelte

Dans le précédent article j’ai présenté Stencil, un framework que j’ai qualifié d’évanescent parce qu’il disparaît à la compilation pour ne laisser subsister qu’un composant web classique, enfin presque classique… Toutefois c’est quand même Svelte qui a plus de succès actuellement pour faire la même chose. Lui aussi a une phase de compilation pour générer du code directement compréhensible par les navigateurs. D’autre part il évolue rapidement et en est actuellement à la version 3.

Si on fait abstraction des « grands » frameworks (React, Vue, Angular…), et qu’on regarde un peu les statistiques, on trouve Svelte en tête de l’intérêt des développeurs, suivi quand même de près par Vue et React :

Je pense que l’intérêt ne pourra que croître à l’avenir pour ce type de librairie.

D’autre part je dois dire que le site de Svelte est un modèle du genre avec un tutoriel interactif qu’on aimerait trouver pour toutes les librairies, une collection d’exemples très complète, une parfaite documentation pour l’API, et un REPL très efficace. On peut donc se former facilement à l’utilisation de cette librairie. Dans cet article je vais me limiter à une présentation générale et surtout montrer comment l’utiliser avec Laravel.

On se lance

Svelte propose donc un REPL bien pratique, c’est une console interactive qui permet de créer une application Svelte. Commençons par l’incontournable Hello World :

Comme j’aime bien voir de près comment les choses sont organisées on va télécharger le code généré avec le bouton prévu pour cet usage et décompresser dans un dossier :

On va charger les dépendances :

npm install

Et ensuite lancer en mode développement :

npm run dev

L’application est disponible à l’adresse http://localhost:5000 :

On trouve les éléments générés :

Svelte utilise Rollup comme bundler. On se retrouve donc avec un simple fichier à charger (le map sert au débogage).

Je rappelle qu’il n’y a plus de framework à l’utilisation mais du Javascript parfaitement compréhensible pour les navigateurs, du moins ce qui ne relèvent pas du musée.

Un peu de style

On peut ajouter du style dans le composant sans influer sur l’environnement :

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

<style>
  h1 {
    color: red;
    font-size: 50px;
  }
</style>

<h1>Hello {name}!</h1>

Des composants imbriqués

On peut imbriquer des composants en les gardant parfaitement isolés. On va créer un composant enfant :

Avec ce simple code :

<h1>Je suis un enfant</h1>

Et l’insérer dans le composant principal (App) :

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

<style>
  h1 {
    color: red;
    font-size: 50px;
  }
</style>

<h1>Hello World !</h1>
<Child/>

Il suffit d’importer le composant et de l’insérer directement dans le HTML. Au passage on voit bien l’isolation du style.

Reactif ?

Ce qu’on attend de ce type de librairie c’est de la réactivité :

<script>
  let count = 0;
  function handleClick() { ++count; }
</script>

<button on:click={handleClick}>
  On a cliqué {count} fois
</button>

On peut optimiser le code en prévoyant le traitement directement dans la balise :

<button on:click="{ () => ++count }">
  On a cliqué {count} fois
</button>

Bon ce n’est pas le genre de syntaxe que j’aime mais avec Svelte (et avec d’autres) on doit se plier à cette forme de codage qui rappelle les débuts du codage.

Au niveau de la réactivité on a aussi un syntaxe un peu exotique pour rendre des déclarations réactives :

<script>
  let count = 0;
  $: odd = count % 2 ? 'impair' : 'pair';
</script>

<button on:click="{ () => ++count }">
  On a cliqué {count} fois et c'est {odd}
</button>

Lorsqu’on utilise $: on a une régénération lorsqu’il y a un changement sinon la valeur calculée ne serait pas répercutée.

Les propriétés

On doit pouvoir transmettre des valeurs à un composant, c’est là qu’interviennent les propriétés (props). On crée un composant enfant (Child) :

<script>
  export let name;
</script>

<h1>Je m'appelle { name }</h1>

Remarquez la syntaxe avec export.

Dans le composant parent (App) on importe le composant enfant et on assigne la propriété :

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

<Child name={ 'Martin' }/>

On peut prévoir une valeur par défaut :

export let name = 'Dupont';

On peut même traiter le cas particulier d’un objet, donc d’une référence,on peut détailler :

// Child
<script>
  export let nom;
  export let prenom;
</script>

<h1>Mon nom est { nom } et mon prénom { prenom }</h1>

// App
<script>
 import Child from './Child.svelte';

 const fullName = {
   nom: 'Martin',
   prenom: 'Jacques'
 };
</script>

<Child nom={ fullName.nom } prenom={ fullName.prenom }/>

Mais on peut aussi utiliser une affectation par décomposition :

<Child { ...fullName }/>

Les opérateurs conditionnels

On dispose aussi des principaux opérateurs conditionnels, par exemple :

<script>
 const name = 'Martin';
</script>

{ #if name.length < 5 }
  <p>{ name } est un nom court</p>
{ :else if name.length < 10 }
  <p>{ name } est un nom de longueur moyenne</p>
{ :else }
  <p>{ name } est un nom long</p>
{ /if }
Martin est un nom de longueur moyenne

Svelte sait aussi traiter efficacement les listes :

<script>
  let marques = [
    { name: 'Renault' },
    { name: 'Peugeot' },
    { name: 'Mercedes' }
  ];
</script>

<h1>Les marques de voitures :</h1>
<ul>
  { #each marques as marque }
    <li>{ marque.name }</li>
  { /each }
</ul>
Les marques de voitures :
Renault
Peugeot
Mercedes

Les événements

Un composant enfant peut envoyer des informations à son parent en émettant un événement :

// Child
<script>
  import { createEventDispatcher } from 'svelte';

  const dispatch = createEventDispatcher();

  function send() {
    dispatch('message', { text: 'Bouton actionné !' });
  }
</script>

<button on:click={send}>
  Appuyer ici !
</button>

// App
<script>
  import Child from './Child.svelte';

  function handleMessage(event) {
    alert(event.detail.text);
  }
</script>

<Child on:message={handleMessage}/>

Les liaisons

Quand on a des zones de saisie de formulaires il est pratique de mettre automatiquement à jour une propriété quand la valeur saisie change, donc de lier les deux. Svelte permet de le faire facilement :

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

<input bind:value={name} placeholder="Votre nom ici">
<p>Bonjour {name || 'étranger'}!</p>

Je vous encourage à voir toutes les liaisons possibles dans la documentation.

Le cycle de vie des composants

On a des étapes dans la vie d’un composant entre sa création et sa destruction, on a des fonctions utilisable selon le moment de la vie d’un composant :

  • onMount : lorsque le composant est intégré au DOM
  • onDestroy : c’est l’inverse, lorsque le composant est détruit
  • beforeUpdate :  avant que le DOM ne soit modifié
  • afterUpdate : c’est l’inverse, après que le DOM a été modifié
  • tick : une fonction qu’on peut appeler n’importe quand et qui renvoie une promesse résolue dès qu’il n’y a plus de changements en attente pour le DOM

Mouvements, transitions et animations

Svelte est bien pourvu aussi en possibilités dynamiques. Il sait gérer des transitions visuelles paramétrables et des animations. Là aussi je vous laisse consulter les exemples sur le site qui offrent immédiatement le résultat visuel !

Laravel

Il y a encore plein de choses à découvrir avec Svelte mais je n’irai pas plus loin dans cette simple présentation. Par contre on va voir à présent comment utiliser Svelte avec Laravel.

On commence par installer une version fraîche de Laravel :

composer create-project laravel/laravel larasvelte --prefer-dist

On installe aussi le package complémentaire :

composer require laravel/ui --dev

Ensuite on utilise le preset Bootstrap avec l’authentification (en admettant qu’on veuille utiliser Bootstrap comme framework CSS) :

php artisan ui bootstrap --auth

On va ensuite installer le preset pour Svelte :

composer require wewowweb/laravel-svelte-preset

Et activer ce preset :

php artisan preset svelte

Il faut ensuite installer les dépendances :

npm install

Et pour finir générer en mode développement :

npm run dev

On remarque au passage qu’il a été installé un plugin Svelte pour mix :

"devDependencies": {
    ...
    "laravel-mix-svelte": "^0.1.0",

Voyons ce qu’on a généré…

Déjà un fichier app.js :

Avec ce code :

require('./bootstrap');

import App from "./components/App.svelte";

const app = new App({
  target: document.body
});

window.app = app;

export default app;

On voit qu’on importe un composant Svelte (App) :

Avec ce code de base :

<script>
  import { onMount } from "svelte";

  onMount(() => {
    console.log("the component has mounted");
  });
</script>

<main>
  <div class="container">
    <div class="row justify-content-center">
      <div class="col-md-8">
        <div class="card">
          <div class="card-header">Example Component</div>
          <div class="card-body">
            I'm an example component.
          </div>
        </div>
      </div>
    </div>
  </div>
</main>

Et dans webpack.mix.js on trouve le chargement du plugin pour Svelte :

const mix = require('laravel-mix');
require('laravel-mix-svelte');
...

Pour voir ce composant en action et vérifier que ça fonctionne on crée une nouvelle vue :

On va lui mettre ce code :

@extends('layouts.app')

@section('content')
  <App/>
@endsection

Et une route pour y accéder :

Route::view('test', 'test');

Et voilà on a intégré le composant Svelte dans la page !

On peut maintenant ajouter les composants Svelte facilement. Faisons un petit essai avec un exemple du site. On crée un composant Box :

Avec ce code :

<style>
  .box {
    width: 300px;
    border: 1px solid #aaa;
    border-radius: 2px;
    box-shadow: 2px 2px 8px rgba(0,0,0,0.1);
    padding: 1em;
    margin: 0 0 1em 0;
  }
</style>

<div class="box">
  <slot></slot>
</div>

Et on change le code de App.svelte ainsi :

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

<main>
  <div class="container">
    <div class="row justify-content-center">
      <div class="col-md-8">
        <Box>
          <h2>Salut !</h2>
          <p>C'est une simple boîte !</p>
        </Box>
      </div>
    </div>
  </div>
</main>

Et on relance mix :

npm run dev

Et si tout s’est bien passé :

Au passage on en profite pour voir que Svelte utilise la notion de slot qu’on retrouve par exemple avec Blade.

Conclusion

On a vu que Svelte est une librairie bien équipée et montre un peu l’avenir du développement côté client. Le fait de procéder à une compilation pour générer des composants ne nécessitant pas de framework est une grande idée, on se demande finalement pourquoi on n’y a pas pensé plus tôt !

Pour être complet je dois ajouter qu’il existe un package pour ajouter un routeur.

Alors faut-il se lancer dans Svelte et abandonner React et Vue parce qu’ils appartiennent à un monde révolu ? La question est posée et je n’ai pas la réponse définitive. D’ailleurs je me demande finalement si on ne va pas arriver à se passer complètement de librairie parce que les API des navigateurs nous offriront tout ce dont nous avons besoin…

Print Friendly, PDF & Email

Laisser un commentaire