Introduzione a Svelte

Svelte è un framework front-end diretto concorrente di altri mostri sacri come Vue, React e Angular che fa della compilazione il suo punto di forza.

# Introduzione

Svelte è un framework front-end diretto concorrente di altri mostri sacri come Vue, React e Angular che fa della compilazione il suo punto di forza: Svelte converte completamente la nostra applicazione in codice JavaScript efficiente durante il build della stessa, scomparendo completamente a runtime.

# Creazione di un progetto

Per creare un progetto utilizzeremo un tool che creerà un template con tutto il necessario:

npx degit sveltejs/template template-master

Questo comando creerà una cartella dal nome template-master, nella quale dovremo entrare:

cd template-master

per poter installare le dipendenze necessarie:

npm i

Il comando che ci permette di testare la nostra applicazione in locale è il seguente:

npm run dev

La troveremo in http://localhost:5000/.

# Struttura del progetto e dei file

La sezione che ci interessa particolarmente è la cartella /src, la quale conterrà i componenti che andremo a creare. Ogni componente risiederà in un file avente per estensione .svelte.

Ogni componente è composto da tre sezioni principali:

<script>
</script>

<main>
</main>

<style>
</style>

La sezione script ci permetterà di dichiarare le variabili d'istanza dei nostri componenti, eventuali metodi e prop. Nel main inseriremo il markup da renderizzare, mentre in style andrà il CSS relativo al componente.

Al momento dentro la cartella /src vi è solo un componente, App, il quale contenuto andiamo a sostituire con:

<script>
 let message = "Hello World!";
</script>

<main>
 <h1>{message}</h1>
</main>

Complimenti! Hai appena scritto il tuo Hello World in Svelte!

# Reattività

Analizzando con più attenzione il contenuto possiamo notare la dichiarazione di una variabile d'istanza, message, la quale viene interpolata all'interno del markup tramite la sintassi {}.

Che dire se volessimo modificare il valore di message, ad esempio con un elemento <input>? Nulla di più semplice:

<script>
 let message = "Hello World!";
</script>

<main>
    <h1>{message}</h1>
    <input bind:value={message}>
</main>

Questa direttiva particolare crea un two-way data flow tra la variabile d'istanza message e il contenuto del tag <input>. Ciò significa che scrivendo nel box di input modificheremo il valore di message, ma anche che se altri attori in gioco dovessero a loro volta cambiare message, il contenuto del box di input verrà aggiornato di conseguenza.
E non dimentichiamo che ogni modifica a message si riflette automaticamente anche nel contenuto del tag <h1>.

Svelte ci da anche la possibilità di dichiarare variabili reattive, ovvero variabili il quale valore viene ricalcolato ogni volta che una delle loro dipendenze si aggiorna. Aggiungiamo la seguente dichiarazione alla sezione <script>:

<script>
 let message = "Hello World!";
    $: capitalizedMessage = message.toUpperCase();
</script>

E modifichiamo leggermente il markup:

<main>
 <h1>{message}</h1>
 <h2>{capitalizedMessage}</h2>
 <input bind:value={message}>
</main>

Non abbiamo fatto altro che aggiungere una variabile reattiva, capitalizedMessage, la quale sarà sempre allineata con il valore corrente di message, con l'unica differenza che ogni carattere alfabetico sarà in maiuscolo.

# Componenti, props ed eventi

Creiamo ora un semplice componente Button, il quale avrà una prop text per impostare esternamente il testo del bottone ed emetterà un evento click ogni volta che viene premuto.

Iniziamo creando un file Button.svelte nella cartella /src con il seguente contenuto:

<script>
 export let text;
</script>

<main>
 <button>{text}</button>
</main>

La keyword export non è utilizzata per errore; in Svelte indica che text è una prop, esattammente come richiesto.
Adesso faremo in modo che il <button> stia in ascolto dell'evento nativo click e lo riemetta:

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

    const dispatch = createEventDispatcher();

    function emitClick() {
        dispatch('click')
    }

    export let text;
</script>

<main>
 <button on:click={emitClick}>{text}</button>
</main>

Per poter emettere eventi è necessario creare una funzione di dispatch come mostrato nell'esempio, per poi utilizzarla all'interno del markup. Il modo migliore è sicuramente quello di creare una funzione ad-hoc che ne fa internamente uso, cioè emitClick, impostandola come listener nel bottone utilizzando la direttiva on:click.

Spostiamoci ora nuovamente in App.svelte e vediamo come poter utilizzare il componente Button. Innanzitutto dobbiamo importarlo:

<script>
    import Button from "./Button.svelte"

 // ...
</script>

ed inserirlo nel markup:

<main>
    <!-- -->

 <Button/>
</main>

Per impostare il valore della prop text è sufficiente scrivere così:

<main>
    <!-- -->

 <Button text="Click Me"/>
</main>

Oppure possiamo far uso della sintassi {} e renderla dinamica:

<main>
    <!-- -->

 <Button text={message}/>
</main>

Infine, per ascoltare l'evento click emesso la sintassi è identica a quella precedentemente usata nel componente Button:

<script>
 // ...

    function clickHandler() {
        console.log("The button has been clicked");
    }
</script>

<main>
    <!-- -->

 <Button text={message} on:click={clickHandler}/>
</main>

# Rendering condizionale

Vediamo ora la sintassi da utilizzare per renderizzare un dato componente o elemento HTML in base al valore di verità di una variabile. Aggiungiamo il seguente codice nella sezione script:

<script>
    // ...

    $: greaterThan5 = message.length > 5;
</script>

La variabile reattiva greaterThan5 sarà true se e solo se message contiene almeno 6 caratteri, false altrimenti. Nel markup rendiamo opzionale la renderizzazione del Button nel seguente modo:

<main>
    <!-- -->

    {#if greaterThan5}
     <Button text={message} on:click={clickHandler}/>
    {/if}

</main>

# Rendering Iterativo

Infine accenniamo alla possibilità di renderizzare un insieme dinamico di elementi senza duplicare inutilmente il codice. Immaginiamo di avere la seguente lista di persone:

<script>
    // ...

    let people = [
            { id: '3TauxZZGyEg', name: 'Piero Salvini' },
            { id: 'wuRNsybk8To', name: 'Adriano Celentano' },
            { id: '0OuxhV5Kz8Y', name: 'Giovanni Morandi' },
    ];
</script>

Possiamo far uso di un each block all'interno del markup per renderizzare ogni nome:

<main>
    <!-- -->
    {#each people as person}
		<li><a target="_blank" href="https://www.youtube.com/watch?v={person.id}">
			{person.name}
		</a></li>
	{/each}
</main>

Ogni singola persona sarà disponibile come person, grazie al quale possiamo utilizzare id e name. All'interno dell'each block potremo inserire il markup che preferiamo, il quale verrà ripetuto eguale per ogni persona.