Tutorial VueJS 3: componenti e props con defineProps
verificato il 2 Settembre 2026 · 5 min
Un componente è un file .vue con il suo template, il suo stato e la sua logica. Importalo in <script setup> per usarlo direttamente nel template. Il genitore gli passa i dati tramite le props, dichiarate con defineProps e collegate con i due punti.
Da sette capitoli tutto sta in un unico file. Così non si scala. Un componente è un pezzo di interfaccia autonomo, con il suo template, il suo stato e la sua logica, che si riutilizza e si annida.
Estrarre la scheda prodotto
Crea src/components/ProductDisplay.vue e spostaci tutto quello che riguarda il prodotto: l’immagine, il titolo, la disponibilità, i dettagli, i pallini dei colori, il pulsante.
<script setup>
import { computed, ref } from 'vue'
const product = ref('T-Shirt')
const brand = ref('Gekkode')
const selectedVariant = ref(0)
const details = ref(['60 % coton', '30 % laine', '10 % polyester'])
const variants = ref([
{ id: 2234, color: '#2563eb', image: '/images/t-shirt-bleu.svg', quantity: 20 },
{ id: 2235, color: '#dc2626', image: '/images/t-shirt-rouge.svg', quantity: 0 }
])
const title = computed(() => `${brand.value} ${product.value}`)
const image = computed(() => variants.value[selectedVariant.value].image)
const inStock = computed(() => variants.value[selectedVariant.value].quantity > 0)
function updateVariant(index) {
selectedVariant.value = index
}
</script>
<template>
<div class="product-display">
<div class="product-image">
<img :src="image" :alt="title">
</div>
<div class="product-info">
<h1>{{ title }}</h1>
<p v-if="inStock">En stock</p>
<p v-else>En rupture</p>
<ul>
<li v-for="detail in details" :key="detail">{{ detail }}</li>
</ul>
<div class="variants-wrapper">
<div
v-for="(variant, index) in variants"
:key="variant.id"
class="color-circle"
:style="{ backgroundColor: variant.color }"
@mouseover="updateVariant(index)"
></div>
</div>
<button class="button" :class="{ disabledButton: !inStock }" :disabled="!inStock">
Ajouter au panier
</button>
</div>
</div>
</template>Il carrello non lo segue: non appartiene al prodotto. App.vue si riduce a:
<script setup>
import { ref } from 'vue'
import ProductDisplay from './components/ProductDisplay.vue'
const cart = ref(0)
</script>
<template>
<div class="cart">Panier ({{ cart }})</div>
<ProductDisplay />
</template>Un componente importato in <script setup> è utilizzabile subito nel template: nessuna dichiarazione in più.
È normale, ed è l’argomento del capitolo successivo. cart ora vive nel genitore, fuori dalla portata del componente figlio. Abbiamo anche tolto il @click dal pulsante: lasciarlo avrebbe prodotto un errore, visto che addToCart non esiste più in questo componente.
Dare un nome a un componente
Coesistono due convenzioni. In PascalCase, <ProductDisplay />, distingui al volo un componente da un tag HTML e l’editor riesce a saltare alla sua definizione. In kebab-case, <product-display></product-display>, resti compatibile con un template scritto direttamente in una pagina HTML.
In un file .vue, preferisci il PascalCase.
Ogni componente ha il proprio scope
Aggiungi tre schede in un colpo solo:
<template>
<ProductDisplay />
<ProductDisplay />
<ProductDisplay />
</template>Compaiono tre schede, indipendenti: passare il mouse su un pallino della prima non tocca le altre. Ogni istanza esegue il proprio <script setup> e ha il proprio selectedVariant.
Ogni scheda mantiene quindi anche la sua variante selezionata.
È anche il motivo per cui lo stato di un componente si dichiara dentro una funzione. Se variants fosse un semplice oggetto condiviso a livello di modulo, le tre schede si pesterebbero i piedi.
Togli le due schede di troppo prima di continuare.
Le props
Lo scope isolato pone una domanda: come fa il genitore a passare un’informazione al figlio? Con una prop, un attributo personalizzato che il componente dichiara di accettare.
Aggiungiamo la spedizione, gratuita per i clienti premium. L’informazione appartiene al genitore:
<script setup>
const premium = ref(true)
</script>
<template>
<div class="cart">Panier ({{ cart }})</div>
<ProductDisplay :premium="premium" />
</template>E il figlio la dichiara:
<script setup>
const props = defineProps({
premium: { type: Boolean, required: true }
})
const shipping = computed(() => (props.premium ? 'Gratuite' : '2,99 €'))
</script>
<template>
<p>Livraison : {{ shipping }}</p>
</template>defineProps è disponibile senza import in <script setup>: è una macro del compilatore, non una funzione normale.
Nel template si scrive {{ premium }} direttamente. Nello script bisogna passare dall’oggetto restituito: props.premium.
Dichiarare le props sul serio
La forma lunga permette di validare:
const props = defineProps({
premium: { type: Boolean, required: true },
titre: { type: String, default: 'Sans titre' },
tailles: { type: Array, default: () => [] },
note: {
type: Number,
validator: (valeur) => valeur >= 0 && valeur <= 5
}
})Il valore predefinito di un array o di un oggetto deve essere restituito da una funzione, altrimenti tutte le istanze condividerebbero lo stesso oggetto.
:premium="premium" passa il valore della variabile. premium="premium", senza i due punti, passa la stringa "premium". Siccome una stringa non vuota è vera, il bug passa inosservato fino al giorno in cui il valore dovrebbe essere falso. Vue del resto avverte in console che si aspettava un tipo Boolean e ha ricevuto String.
Le props scendono, non risalgono
Una prop viaggia in un solo senso: dal genitore al figlio. Il figlio non deve modificarla, se ci provi Vue mostra un avviso.
Se il figlio ha bisogno di una versione locale modificabile, copiala in un ref. Se deve provocare un cambiamento nel genitore, deve mandargli un segnale: è l’argomento del capitolo successivo.
Le props si dichiarano nell’opzione props, con la stessa sintassi di validazione, e si leggono con this.premium. I componenti figli vanno inoltre registrati nell’opzione components.
ProductDetails.vue che riceve l'array come prop. Poi mostra due schede prodotto e verifica che ognuna abbia davvero la propria variante selezionata.Errori frequenti
premium="premium" passa la stringa «premium», sempre vera. Scrivi :premium="premium". Vue del resto segnala un tipo Boolean atteso e String ricevuto.