React

Composition API di Vue 3: ref, computed e composable

La Composition API sposta stato e logica dentro setup(): tre primitive, ref, computed e composable, coprono la maggior parte dei componenti Vue 3.

In sintesi

  • setup() è il punto di ingresso: il template vede solo ciò che la funzione restituisce.
  • Un composable è una funzione use* che restituisce ref: non è uno store e non condivide stato.
  • Destrutturare un oggetto reactive rompe la reattività; i ref sopravvivono perché sono oggetti.

La Composition API di Vue 3 sposta stato e logica dentro setup(): le stesse funzionalità delle opzioni, scritte come funzioni invece che come campi di un oggetto. Non è un secondo framework, è la stessa istanza di componente con un punto di ingresso diverso.

Tre primitive e un punto di ingresso

ref() crea un contenitore reattivo per un valore singolo: nello script leggi e scrivi con .value, nel template Vue lo scarta da solo. reactive() fa lo stesso su un oggetto, ma perde la reattività se lo destrutturi. computed() deriva un valore e lo ricalcola solo quando cambia una dipendenza.

npm create vue@latest

Il generatore ufficiale propone TypeScript, Vue Router e Pinia: prendi TypeScript, rimanda Pinia a quando lo stato deve attraversare le route. I componenti nascono con <script setup>, la forma compatta della Composition API.


import { computed, ref } from "vue";

const price = ref(0);
const qty = ref(1);
const total = computed(() => price.value * qty.value);



  
  Total: {{ total.toFixed(2) }}

Un composable è una funzione, non uno store

Un composable è una funzione che chiama altre primitive e restituisce ref: si importa, si chiama dentro setup() e crea stato nuovo a ogni chiamata. Il prefisso use è una convenzione, non un meccanismo. Nell’esempio il cleanup annulla la richiesta precedente quando l’id cambia o il componente smonta.

// composables/useUser.ts
import { ref, watch } from "vue";

export function useUser(id: () => string) {
  const user = ref(null);
  const loading = ref(false);

  watch(
    id,
    async (value, _old, onCleanup) => {
      const controller = new AbortController();
      onCleanup(() => controller.abort());
      loading.value = true;
      const res = await fetch("/api/users/" + value, { signal: controller.signal });
      user.value = await res.json();
      loading.value = false;
    },
    { immediate: true },
  );

  return { user, loading };
}

Dove conviene e dove no

CasoVincePerché
Logica riusata in tre componentiComposition APIUn composable al posto di tre mixin
Componente fatto di sole propsOptions APINon c’è logica da estrarre
Stato condiviso fra routeNé l’una né l’altraServe Pinia, non un composable
Test della sola logicaComposition APILa funzione si importa senza montare il componente

Errori che rompono la reattività

  • Destrutturare le props o un oggetto reactive: il valore si copia e smette di aggiornarsi.
  • Chiamare un composable fuori da setup(): gli hook di ciclo di vita non hanno istanza a cui agganciarsi.
  • Scrivere .value nel template: Vue lo scarta già, e il doppio scarto restituisce un valore vuoto.
  • Calcolare un valore derivato dentro watchEffect: computed è dichiarativo e più economico.

Il limite da mettere in conto

La Composition API non decide dove vive lo stato. Un composable chiamato in due componenti crea due stati indipendenti: se ti aspetti un valore condiviso è un errore di progetto, non del framework. Per lo stato che attraversa le route serve uno store Pinia.

I componenti del catalogo sono già scritti con questa API: shadcn-vue, vue-bits e PrimeVue si installano come sorgente nel tuo repository. Il confronto con React è in Vue vs React; la scelta della libreria in librerie UI per Vue 3.

Domande frequenti

Devo riscrivere i componenti esistenti?

No. Le due API convivono: un componente a opzioni può montare un composable e un componente con setup può usare un mixin. Riscrivi quando devi cambiare la logica, non per coerenza estetica.

ref o reactive?

ref per valori primitivi e per oggetti che passi in giro, reactive per un gruppo di campi che restano insieme. Se destrutturi spesso, ref costa meno.

Perché il valore smette di aggiornarsi dopo la destrutturazione?

Perché copi il valore corrente invece di tenere il riferimento. Con un oggetto reactive serve toRefs; con un ref il campo value resta collegato.

Un composable può sostituire Pinia?

No. Un composable chiamato in due punti crea due stati separati: va bene per logica riusata, non per un carrello condiviso. Per quello serve uno store con una istanza sola.

La Composition API è più lenta?

No. Il costo di rendering è lo stesso, cambia il modo in cui dichiari stato e dipendenze. La differenza pratica è che un composable scritto male crea più ref del necessario.