L’uso dei framework JavaScript, come Vue.js, risponde alla necessità di gestire la complessità delle applicazioni web moderne, fornendo una struttura che facilita lo sviluppo.
L’obiettivo di questi strumenti è semplificare lo sviluppo, migliorare la manutenibilità del codice e offrire un’esperienza utente più fluida.
Nota importante
I framework non sono una bacchetta magica. Richiedono tempo per essere appresi e utilizzati efficacemente. Non aspettatevi di diventare esperti in Vue.js/React/Angular in poche ore.
In questa lezione vedremo solamente i concetti fondamentali, applicando quanto abbiamo appreso finora di JavaScript. Per diventare proficienti con un framework in particolare è necessario approfondirlo nello specifico.
Partiamo da JavaScript puro. Un contatore classico:
Funziona. Ma se l’app cresce?
Con JS puro, all’aumentare della complessità:
count, items, user sparse nel codicequerySelector + updatetextContent da aggiornareVue.js è un framework JavaScript progressivo per costruire interfacce utente.
Possiamo iniziare a usare Vue senza setup complessi: è sufficiente includere la libreria via CDN:
oppure, se vogliamo usare i moduli ECMAScript:
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<title>Prima app Vue</title>
</head>
<body>
<div id="app">
<h1>{{ messaggio }}</h1>
</div>
<script type="module">
import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js';
createApp({
data() {
return {
messaggio: 'Ciao Vue!'
};
}
}).mount('#app');
</script>
</body>
</html>Ogni applicazione Vue inizia creando una nuova istanza dell’applicazione con la funzione createApp:
L’oggetto che passiamo a createApp è in realtà un componente. Ogni app richiede un “componente radice” che può contenere altri componenti come suoi figli.
Un’istanza dell’applicazione non renderizzerà nulla fino a quando non verrà chiamato il suo metodo .mount(). Questo metodo si aspetta un argomento “contenitore”, che può essere sia un elemento reale del DOM sia un selettore di stringa:
Il contenuto del componente root dell’app verrà renderizzato all’interno dell’elemento contenitore.
Il metodo .mount() dovrebbe sempre essere chiamato dopo che siano state effettuate tutte le configurazioni dell’app e le registrazioni delle risorse.
Vediamo un esempio con un pulsante che incrementa un contatore:
Possiamo notare l’assenza di qualsiasi codice che manipoli direttamente il DOM. Niente querySelector. Niente textContent. Niente updateDOM().
Vue utilizzerà automaticamente l’innerHTML del contenitore come template se il componente root non ha già un’opzione template.
In Vue, lo stato (i dati dentro data()) è reattivo: quando cambia, il DOM si aggiorna automaticamente.
Vue fa questo tracciando quali proprietà dello stato usa il template. Se count compare tra doppie parentesi graffe {{ }}, Vue sa che il template dipende da count.
Quando count cambia, Vue:
countQuesto comportamento è trasparente: non dobbiamo fare nulla di speciale. Basta usare le proprietà di data() nel template, e Vue fa il resto.
Nel template, inseriamo testo e logica usando la sintassi mustache {{ }}:
Ogni volta che una proprietà di data() usata in {{ }} cambia, Vue aggiorna il template.
All’interno di {{ }} è possibile scrivere espressioni JavaScript arbitrarie:
{{ messaggio }} interpola il valore di data().messaggio{{ nomeMetodo() }} chiama un metodo e mostra il risultato{{ count + 1 }}, {{ connesso ? 'online' : 'offline' }}Tuttavia, non è possibile scrivere istruzioni come if, for, while dentro {{ }}.
Per questo ci sono le direttive (che vedremo tra poco).
JS puro (imperativo)
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<title>JS puro - esempio</title>
</head>
<body>
<button id="btn">+</button>
<p id="count">0</p>
<script>
let count = 0;
const btn = document.getElementById('btn');
const el = document.getElementById('count');
btn.addEventListener('click', () => {
count++;
// devo aggiornare il DOM manualmente
el.textContent = count;
});
</script>
</body>
</html>Dico come aggiornare il DOM.
Vue (dichiarativo)
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<title>Vue - esempio</title>
</head>
<body>
<div id="app">
<button @click="count++">+</button>
<p>Conteggio: {{ count }}</p>
</div>
<script type="module">
import { createApp } from 'https://unpkg.com/vue@3';
createApp({
data() {
return { count: 0 }
}
}).mount('#app');
</script>
</body>
</html>Dico cosa mostrare. Vue aggiorna il DOM da solo.
Consiglio
Partendo dall’app contatore vista sopra:
- che decrementa countReset che riporta count a zeroOutput atteso: tre pulsanti funzionanti, il contatore si aggiorna senza toccare il DOM manualmente.
v-*Una direttiva è un normale attributo HTML con prefisso v- che viene interpretato in modo speciale da Vue, permettendo di applicare un comportamento reattivo al DOM.
v- indica che si tratta di una direttiva Vue.prevent per @submit.preventCome vedremo, alcune direttive possiedono anche una forma abbreviata per comodità.
Le parentesi graffe non possono essere utilizzate all’interno degli attributi HTML. Si può usare, invece, la direttiva v-bind:
Dato che v-bind è utilizzato così di frequente, ha una sintassi abbreviata dedicata, che consiste nel sostituire v-bind: con solamente i due punti “:”
La direttiva v-bind indica a Vue di mantenere sincronizzato l’attributo id dell’elemento con la proprietà dinamica dynamicId del componente.
Se il valore associato è null o undefined, l’attributo verrà rimosso dall’elemento renderizzato. Per gli attributi booleani, come disabled, anche se il valore è falsy, l’attributo non sarà presente.
La direttiva v-if viene utilizzata per renderizzare in maniera condizionale un blocco di codice. Il blocco verrà renderizzato nel DOM solo se l’espressione della direttiva restituisce un valore truthy.
Ovviamente esistono anche le direttive v-else-if e v-else per indicare “blocchi alternativi” a v-if.
Esiste anche una direttiva v-show che nasconde un elemento invece di rimuoverlo dal DOM. La scelta tra v-if e v-show dipende dalle esigenze specifiche dell’applicazione.
Per mostrare una lista di elementi basati su un array possiamo utilizzare la direttiva v-for, che itera su un array e ripete un elemento per ogni valore.
La direttiva v-for utilizza una sintassi nella forma di item in items, dove items è l’array di dati sorgente e item è un alias per l’elemento dell’array su cui si sta iterando:
<div id="app">
<ul>
<li v-for="ricetta in ricette" :key="ricetta.id">
{{ ricetta.nome }} — {{ ricetta.categoria }}
</li>
</ul>
</div>
<script>
createApp({
data() {
return {
ricette: [
{ id: 1, nome: 'Pasta al pomodoro', categoria: 'primo' },
{ id: 2, nome: 'Tiramisù', categoria: 'dolce' }
]
}
}
}).mount('#app')
</script>La direttiva v-on (abbreviata con la chiocciola @) collega un evento DOM a un handler.
@click="count++" è un inline handler: l’espressione viene eseguita direttamente nel templatev-on:click è la forma completa; @click è l’abbreviazione@click="console.log('Ciao')"Gli inline handler sono comodi per azioni semplici.
Quando dobbiamo svolgere azioni più complesse (validazione, calcoli, più linee), possiamo definire dei metodi all’interno della proprietà methods:
<div id="app">
<button @click="incrementa">+</button>
<button @click="decrementa">-</button>
<button @click="azzera">Reset</button>
<p>Conteggio: {{ count }}</p>
</div>
<script>
createApp({
data() {
return { count: 0 }
},
methods: {
incrementa() { this.count++ },
decrementa() { this.count-- },
azzera() { this.count = 0 }
}
}).mount('#app')
</script>Nei metodi, possiamo accedere allo stato del componente tramite this.
I modificatori sono suffissi speciali che modificano il comportamento di una direttiva.
<form @submit.prevent="aggiungi">
<input type="text" placeholder="Nome ricetta">
<button type="submit">Aggiungi</button>
</form>
<script>
createApp({
methods: {
aggiungi(event) {
// con .prevent, event.preventDefault() è già stata chiamata
console.log('Form inviato senza refresh!')
}
}
}).mount('#app')
</script>.prevent → chiama event.preventDefault() (evita il refresh della pagina).stop → chiama event.stopPropagation() (ferma la propagazione).once → il listener viene eseguito solo una voltaLe computed properties sono valori derivati dallo stato, calcolati automaticamente.
<div id="app">
<p>Ricette totali: {{ ricette.length }}</p>
<p>Ricette dolci: {{ ricetteDolci }}</p>
</div>
<script>
createApp({
data() {
return {
ricette: [
{ id: 1, nome: 'Tiramisù', categoria: 'dolce' },
{ id: 2, nome: 'Pasta al pomodoro', categoria: 'primo' },
{ id: 3, nome: 'Panna cotta', categoria: 'dolce' }
]
}
},
computed: {
ricetteDolci() {
return this.ricette.filter(r => r.categoria === 'dolce').length
}
}
}).mount('#app')
</script>Vue ricalcola la computed property solo quando le sue dipendenze cambiano (caching automatico).
La direttiva v-model crea un collegamento bidirezionale tra l’input e lo stato:
data() si aggiornaConsiglio
Partendo dall’app contatore con i tre pulsanti:
raddoppia che moltiplica count per 2Output atteso: quattro pulsanti funzionanti. Il metodo usa this.count.
I componenti ci permettono di dividere l’interfaccia utente in pezzi indipendenti e riutilizzabili.
Un componente è un pezzo di UI con il proprio template, i propri dati e il proprio comportamento.
Definizione
Registriamo un componente globalmente con app.component().
I componenti registrati globalmente possono essere utilizzati nel template di qualsiasi componente dell’applicazione.
Le props sono attributi personalizzati che puoi registrare su un componente per passargli dei dati.
Dichiarazione
Le props si dichiarano con un array di stringhe.
Le props formano un legame unidirezionale verso il basso: quando la proprietà genitore si aggiorna, il dato fluisce al figlio, ma non viceversa.
Il vantaggio reale dei componenti emerge quando lavoriamo con liste di dati.
Ogni istanza del componente mantiene il proprio stato separato. La prop :ricetta riceve un oggetto diverso per ogni iterazione.
Consiglio
Riprendiamo il ricettario delle lezioni precedenti e lo ricostruiamo in Vue con stato reattivo e template dichiarativo.
Costruite una mini app ricettario usando tutti i concetti della lezione.
Struttura dati:
Funzionalità da implementare:
data() con la struttura indicatav-for e :key="ricetta.id"v-model + computedx su y (visualizzate su totale)v-model + @submit.prevent@click e metodo dedicatoricetta-card riutilizzabile, passando i dati via propsNiccolò Maltoni