Capitolo 8 di 11

Tutorial VueJS 3: componenti e props con defineProps

verificato il 2 Settembre 2026 · 5 min

Risposta rapida

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.

Alla fine di questo capitolo, saprai dividere un'interfaccia in componenti e passare loro i dati.

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.

src/components/ProductDisplay.vue
<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:

src/App.vue
<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ù.

Il pulsante non riempie più il carrello

È 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:

src/App.vue
<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:

src/App.vue
<script setup>
const premium = ref(true)
</script>

<template>
  <div class="cart">Panier ({{ cart }})</div>
  <ProductDisplay :premium="premium" />
</template>

E il figlio la dichiara:

src/components/ProductDisplay.vue
<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:

javascript
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.

I due punti sono obbligatori

: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.

Con la Options API

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.

EsercizioSposta l'elenco dei dettagli in un componente 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

Dimenticare i due punti su una prop premium="premium" passa la stringa «premium», sempre vera. Scrivi :premium="premium". Vue del resto segnala un tipo Boolean atteso e String ricevuto.
Modificare una prop nel figlio Le props scendono soltanto, Vue avverte in console. Copia il valore in un ref locale, oppure fai risalire un evento.
Valore predefinito di un array o di un oggetto Deve essere restituito da una funzione, altrimenti tutte le istanze condividono lo stesso oggetto.
Newsletter

I nuovi test, tutorial e progetti, via e-mail.

Test riproducibili, codice versionato, risultati datati. Mai spam.