# Composable Avanzati in Vue 3: Pattern Riutilizzabili e Domande da Colloquio 2026 > Guida completa ai composable avanzati di Vue 3 con pattern riutilizzabili, gestione asincrona degli errori, dependency injection con provide/inject e validazione dei form. Include domande da colloquio tecnico aggiornate al 2026. - Published: 2026-05-12 - Updated: 2026-05-12 - Author: SharpSkill - Tags: vue, composables, typescript, interview - Reading time: 12 min --- I composable di Vue 3 rappresentano il meccanismo standard per estrarre e riutilizzare logica reattiva tra componenti. Con l'introduzione degli Alien Signals e della compatibilita con Vapor Mode in Vue 3.6, la padronanza dei pattern di composable influisce direttamente sull'architettura delle applicazioni, sulle performance e sulla manutenibilita del codice nel 2026. > **Cos'e un composable in Vue 3?** > > Un composable e una funzione che sfrutta la Composition API per incapsulare e riutilizzare logica con stato. A differenza dei mixin, i composable forniscono input e output espliciti, inferenza completa dei tipi TypeScript e zero collisioni di nomi. La convenzione prevede il prefisso `use` per i nomi dei composable (ad esempio, `useCounter`, `useFetch`). ## Anatomia di un Composable Ben Strutturato Un composable di livello production segue una struttura prevedibile: accetta la configurazione tramite argomenti, crea lo stato reattivo internamente ed espone un oggetto di ritorno tipizzato. Questo pattern garantisce componibilita, testabilita e confini API chiari. ```typescript // useCounter.ts import { ref, computed, type Ref } from 'vue' interface UseCounterOptions { min?: number max?: number initialValue?: number } interface UseCounterReturn { count: Ref doubled: Ref increment: () => void decrement: () => void reset: () => void } export function useCounter(options: UseCounterOptions = {}): UseCounterReturn { const { min = 0, max = Infinity, initialValue = 0 } = options const count = ref(initialValue) const doubled = computed(() => count.value * 2) function increment() { if (count.value < max) count.value++ } function decrement() { if (count.value > min) count.value-- } function reset() { count.value = initialValue } return { count, doubled, increment, decrement, reset } } ``` Il tipo di ritorno esplicito `UseCounterReturn` svolge due funzioni: documenta l'API pubblica del composable e impedisce l'esposizione accidentale di dettagli implementativi interni. I componenti consumatori destrutturano esattamente cio di cui hanno bisogno, mantenendo trasparenti i binding nel template. Diversi aspetti di questo pattern meritano attenzione. L'oggetto di opzioni con valori predefiniti consente una configurazione flessibile senza sovraccaricare la firma della funzione. Ogni invocazione di `useCounter()` crea un'istanza indipendente di stato, eliminando completamente i problemi di stato condiviso che affliggevano i mixin. Questo pattern "opzioni in ingresso, interfaccia tipizzata in uscita" costituisce la base su cui costruire composable piu complessi. ## Composable Asincroni con Gestione degli Errori e Stati di Caricamento Il data fetching rimane uno dei casi d'uso piu comuni per i composable. Un composable asincrono robusto gestisce lo stato di caricamento, gli errori e la pulizia automatica delle risorse: pattern che i selezionatori esaminano con frequenza durante i colloqui tecnici. ```typescript // useFetchData.ts import { ref, watchEffect, onUnmounted, toValue, type Ref, type MaybeRefOrGetter } from 'vue' interface UseFetchReturn { data: Ref error: Ref isLoading: Ref refresh: () => Promise } export function useFetchData( url: MaybeRefOrGetter ): UseFetchReturn { const data = ref(null) as Ref const error = ref(null) const isLoading = ref(false) let abortController: AbortController | null = null async function fetchData() { // Cancel any in-flight request abortController?.abort() abortController = new AbortController() isLoading.value = true error.value = null try { const response = await fetch(toValue(url), { signal: abortController.signal }) if (!response.ok) throw new Error(`HTTP ${response.status}`) data.value = await response.json() } catch (err) { if (err instanceof DOMException && err.name === 'AbortError') return error.value = err instanceof Error ? err.message : 'Unknown error' } finally { isLoading.value = false } } // Re-fetch when URL changes reactively watchEffect(() => { fetchData() }) onUnmounted(() => abortController?.abort()) return { data, error, isLoading, refresh: fetchData } } ``` Questo composable introduce diversi concetti avanzati. Il tipo di parametro `MaybeRefOrGetter` accetta stringhe semplici, ref o getter, massimizzando la flessibilita per i chiamanti. L'`AbortController` previene le race condition quando l'URL cambia rapidamente, annullando le richieste obsolete prima che la risposta precedente arrivi. La pulizia tramite `onUnmounted` evita memory leak e aggiornamenti di stato su componenti distrutti. Il `watchEffect` stabilisce un tracking reattivo automatico: qualsiasi ref o getter acceduto all'interno del callback diventa una dipendenza. Se l'URL e una ref e il suo valore cambia, l'effetto si riesegue automaticamente. > **Hook del ciclo di vita nei composable** > > I composable che invocano `onMounted`, `onUnmounted` o altri hook del ciclo di vita devono essere chiamati in modo sincrono all'interno di `setup()`. Invocare un composable dentro un callback asincrono o un `setTimeout` fallira silenziosamente nella registrazione dell'hook, poiche non esiste un'istanza di componente attiva in quel momento. ## Composizione di Composable: Costruire Astrazioni di Livello Superiore La vera potenza dei composable emerge quando si compongono tra loro. Questo rispecchia il principio di composizione funzionale: unita piccole e focalizzate combinate in comportamenti complessi senza gerarchie di ereditarieta. ```typescript // usePaginatedSearch.ts import { ref, computed, watch, type Ref } from 'vue' import { useFetchData } from './useFetchData' import { useDebouncedRef } from './useDebouncedRef' interface UsePaginatedSearchReturn { query: Ref page: Ref results: Ref totalPages: Ref isLoading: Ref error: Ref nextPage: () => void prevPage: () => void } export function usePaginatedSearch( baseUrl: string, perPage = 20 ): UsePaginatedSearchReturn { const query = useDebouncedRef('', 300) const page = ref(1) const totalPages = ref(1) const apiUrl = computed( () => `${baseUrl}?q=${encodeURIComponent(query.value)}&page=${page.value}&limit=${perPage}` ) const { data, error, isLoading } = useFetchData<{ items: T[]; total: number }>(apiUrl) const results = computed(() => data.value?.items ?? null) watch(data, (response) => { if (response) { totalPages.value = Math.ceil(response.total / perPage) } }) // Reset to page 1 when query changes watch(query, () => { page.value = 1 }) function nextPage() { if (page.value < totalPages.value) page.value++ } function prevPage() { if (page.value > 1) page.value-- } return { query, page, results, totalPages, isLoading, error, nextPage, prevPage } } ``` La composizione opera su piu livelli. `useDebouncedRef` restituisce una ref con debounce integrato che evita richieste eccessive durante la digitazione. `useFetchData` riceve `apiUrl` come `computed`, il che significa che qualsiasi modifica a `query` o `page` ricalcola l'URL e scatena automaticamente una nuova richiesta. Il watcher su `query` reimposta la paginazione alla pagina 1 ogni volta che il termine di ricerca cambia. Questo pattern dimostra un principio fondamentale: ogni composable risolve un problema specifico e la composizione li connette senza accoppiamento. In un colloquio tecnico, spiegare questa catena di reattivita (`query` cambia -> `apiUrl` si ricalcola -> `useFetchData` si riesegue -> `results` si aggiorna) dimostra una comprensione profonda del sistema reattivo di Vue. ## Dependency Injection con provide/inject per Architetture a Composable Alcuni stati necessitano di essere condivisi attraverso un albero di componenti senza ricorrere al prop drilling. Il sistema `provide/inject` di Vue, combinato con i composable, crea un pattern di contesto tipizzato e sicuro. ```typescript // useTheme.ts import { provide, inject, ref, readonly, type InjectionKey, type Ref } from 'vue' type Theme = 'light' | 'dark' | 'system' interface ThemeContext { theme: Readonly> setTheme: (t: Theme) => void resolvedTheme: Readonly> } const ThemeKey: InjectionKey = Symbol('theme') export function provideTheme(initial: Theme = 'system') { const theme = ref(initial) const resolvedTheme = computed<'light' | 'dark'>(() => { if (theme.value !== 'system') return theme.value return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' }) function setTheme(t: Theme) { theme.value = t } const context: ThemeContext = { theme: readonly(theme), setTheme, resolvedTheme: readonly(resolvedTheme) } provide(ThemeKey, context) return context } export function useTheme(): ThemeContext { const context = inject(ThemeKey) if (!context) { throw new Error('useTheme() requires a parent component to call provideTheme()') } return context } ``` Questo pattern separa la fornitura dal consumo. Il componente radice (o un layout) invoca `provideTheme()` una sola volta, e qualsiasi componente discendente accede al contesto tramite `useTheme()` indipendentemente dalla profondita dell'albero. L'uso di `InjectionKey` con `Symbol` garantisce la sicurezza dei tipi in fase di compilazione. La funzione `readonly()` avvolge le ref esposte per impedire mutazioni dirette dai componenti consumatori. Solo `setTheme()` puo modificare il tema, imponendo un flusso di dati unidirezionale. La proprieta `resolvedTheme` risolve il valore `'system'` consultando la media query del browser, fornendo sempre un valore concreto (`'light'` o `'dark'`) che i componenti possono utilizzare direttamente per applicare gli stili. Nei colloqui tecnici, questo pattern viene spesso confrontato con React Context o gli store di Pinia. La differenza chiave e che `provide/inject` opera a livello dell'albero dei componenti (non e globale) e non richiede dipendenze esterne. ## Composable Riutilizzabile per la Validazione dei Form La validazione dei form e un dominio in cui i composable eccellono particolarmente, sostituendo logica ripetitiva con un pattern dichiarativo basato su regole. ```typescript // useFormValidation.ts import { reactive, computed, type UnwrapNestedRefs } from 'vue' type ValidationRule = (value: T) => string | true type FieldRules = { [K in keyof T]?: ValidationRule[] } interface UseFormReturn> { fields: UnwrapNestedRefs errors: Record isValid: Ref validate: () => boolean resetErrors: () => void } export function useFormValidation>( initialValues: T, rules: FieldRules ): UseFormReturn { const fields = reactive({ ...initialValues }) as UnwrapNestedRefs const errors = reactive( Object.keys(initialValues).reduce( (acc, key) => ({ ...acc, [key]: '' }), {} as Record ) ) function validate(): boolean { let valid = true for (const key of Object.keys(rules) as (keyof T)[]) { const fieldRules = rules[key] || [] errors[key] = '' as any for (const rule of fieldRules) { const result = rule(fields[key]) if (result !== true) { errors[key] = result as any valid = false break // Stop at first error per field } } } return valid } function resetErrors() { for (const key of Object.keys(errors)) { (errors as any)[key] = '' } } const isValid = computed(() => Object.values(errors).every((e) => e === '') ) return { fields, errors, isValid, validate, resetErrors } } ``` Il composable riceve valori iniziali e una mappa di regole di validazione per campo. Ogni regola e una funzione che restituisce `true` se il valore e valido o una stringa con il messaggio di errore. Il metodo `validate()` itera sulle regole e si ferma al primo errore per ciascun campo, evitando di mostrare piu messaggi simultanei che risulterebbero confusionari per l'utente. L'uso di `reactive()` al posto di `ref()` per i campi e gli errori semplifica l'accesso nei template: `fields.email` invece di `fields.value.email`. La proprieta computata `isValid` si aggiorna automaticamente quando cambia qualsiasi errore, consentendo di abilitare o disabilitare i pulsanti di invio in modo reattivo. Questo pattern e estensibile. Si possono aggiungere regole asincrone (verifica di disponibilita di un'email), validazione incrociata tra campi (conferma della password) e messaggi di errore internazionalizzati senza modificare la struttura di base del composable. ## Testing dei Composable in Isolamento I composable che utilizzano le API reattive di Vue richiedono un contesto di componente per funzionare correttamente. La funzione helper `withSetup` risolve questa esigenza creando un componente minimale che esegue il composable all'interno di `setup()`. ```typescript // useCounter.spec.ts import { describe, it, expect } from 'vitest' import { mount } from '@vue/test-utils' import { defineComponent, h } from 'vue' import { useCounter } from './useCounter' function withSetup(composable: () => T): { result: T; unmount: () => void } { let result!: T const wrapper = mount( defineComponent({ setup() { result = composable() return () => h('div') } }) ) return { result, unmount: () => wrapper.unmount() } } describe('useCounter', () => { it('initializes with default value', () => { const { result } = withSetup(() => useCounter()) expect(result.count.value).toBe(0) }) it('respects min and max boundaries', () => { const { result } = withSetup(() => useCounter({ min: 0, max: 3, initialValue: 3 }) ) result.increment() expect(result.count.value).toBe(3) // Capped at max result.count.value = 0 result.decrement() expect(result.count.value).toBe(0) // Capped at min }) it('computes doubled value reactively', () => { const { result } = withSetup(() => useCounter({ initialValue: 5 })) expect(result.doubled.value).toBe(10) result.increment() expect(result.doubled.value).toBe(12) }) }) ``` La funzione `withSetup` e un pattern standard nell'ecosistema Vue per testare composable in isolamento. Monta un componente wrapper che invoca il composable all'interno di `setup()`, fornendo accesso al risultato e una funzione `unmount()` per simulare la distruzione del componente. Ogni test valida un aspetto specifico: inizializzazione con valori predefiniti, rispetto dei limiti configurati e reattivita dei valori computati. Il terzo test e particolarmente importante poiche verifica che `doubled` si aggiorna automaticamente quando `count` cambia tramite `increment()`, confermando il corretto funzionamento del grafo reattivo. Per composable asincroni come `useFetchData`, i test richiedono mock di `fetch` e gestione delle promise con `flushPromises()` di Vue Test Utils. Nei colloqui tecnici, dimostrare familiarita con questo pattern di testing comunica maturita professionale e impegno verso la qualita del codice. > **VueUse come architettura di riferimento** > > VueUse contiene oltre 200 composable di produzione che coprono API del browser, sensori, animazioni e utility. Lo studio del suo codice sorgente rivela pattern coerenti: oggetti di opzioni per la configurazione, protezioni per la compatibilita SSR e `tryOnScopeDispose` per la pulizia delle risorse. Rappresenta un riferimento eccellente per le decisioni architetturali relative ai composable. ## Domande Frequenti da Colloquio sui Composable di Vue 3 Le seguenti domande compaiono regolarmente nei processi di selezione per posizioni Vue di livello intermedio e senior nel 2026. **In che modo i composable differiscono dai mixin, e perche i mixin sono stati deprecati?** I mixin soffrono di tre problemi strutturali: la fusione implicita delle proprieta causa collisioni di nomi, l'origine di dati e metodi diventa opaca nei componenti che utilizzano piu mixin, e TypeScript non riesce a inferire i tipi attraverso la composizione dei mixin. I composable risolvono tutti e tre i problemi restituendo oggetti espliciti, rendendo il flusso dei dati tracciabile e fornendo inferenza completa dei tipi attraverso firme di funzioni standard. **Quando un composable dovrebbe utilizzare `shallowRef` invece di `ref`?** Si utilizza `shallowRef` quando la ref contiene un oggetto o array di grandi dimensioni che viene sostituito interamente piuttosto che mutato. Poiche `shallowRef` traccia solo la riassegnazione (non le modifiche alle proprieta interne), evita il sovraccarico del proxying reattivo profondo. Vue 3.6 utilizza per default la reattivita shallow in determinati contesti proprio per questo motivo prestazionale. **Cosa accade se un composable invoca `onMounted` all'interno di una funzione asincrona?** La registrazione dell'hook del ciclo di vita fallisce silenziosamente. Vue associa gli hook del ciclo di vita all'istanza del componente attualmente attiva, che viene impostata durante l'esecuzione sincrona di `setup()`. Nel momento in cui un callback asincrono viene eseguito, l'istanza attiva potrebbe essere `null` o un componente diverso. La soluzione consiste nell'invocare gli hook del ciclo di vita in modo sincrono al livello superiore del composable e utilizzare `watchEffect` o `watch` per la logica asincrona. **Come interagiscono i composable con il Vapor Mode di Vue nella versione 3.6?** Vapor Mode compila i template in operazioni DOM dirette, bypassando il Virtual DOM. I composable funzionano in modo identico in Vapor Mode poiche operano a livello del layer di reattivita, non del layer di rendering. Le ref reattive e le proprieta computate dei composable attivano aggiornamenti DOM granulari in modo piu efficiente sotto Vapor Mode, rendendo i composable ben strutturati un vantaggio prestazionale. ## Conclusione I composable di Vue 3 rappresentano piu di una semplice alternativa ai mixin: costituiscono un paradigma di organizzazione del codice che scala da funzioni utilitarie semplici a sistemi complessi con dependency injection e composizione multilivello. La padronanza di questi pattern distingue gli sviluppatori che si limitano a utilizzare Vue da coloro che ne comprendono il sistema reattivo in profondita. I punti chiave da consolidare: - **Struttura chiara**: interfacce tipizzate di input e output con `UseXxxOptions` e `UseXxxReturn` documentano l'API e impediscono esposizioni accidentali - **Isolamento dello stato**: ogni invocazione crea un'istanza indipendente, eliminando i conflitti tra consumatori - **Composizione esplicita**: composable che consumano altri composable creano catene di reattivita prevedibili e manutenibili - **Gestione delle risorse**: `AbortController` e `onUnmounted` garantiscono la pulizia degli effetti collaterali e prevengono memory leak - **Injection tipizzata**: `provide/inject` con `InjectionKey` sostituisce il prop drilling senza sacrificare la sicurezza dei tipi - **Testabilita**: il pattern `withSetup` consente di validare la reattivita in isolamento completo rispetto all'interfaccia utente - **Pragmatismo**: verificare VueUse prima di reimplementare funzionalita comuni risparmia tempo e riduce il rischio di bug Preparare questi pattern con implementazioni concrete e la capacita di spiegare le decisioni progettuali alla base di ciascuno fornisce un vantaggio significativo nei processi di selezione tecnica per posizioni Vue nel 2026. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/it/blog/vue-nuxt/advanced-vue-3-composables-reusable-patterns