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.
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/
.
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!
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.
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>
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>
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.