Vue 3 Reactivity Transform nel 2026: $ref, $computed e Domande da Colloquio
Guida completa al Reactivity Transform di Vue 3 con $ref, $computed e $$ macro. Esempi pratici, migrazione da ref() a $ref, e domande frequenti nei colloqui tecnici per sviluppatori Vue senior.

Il Reactivity Transform in Vue 3 rappresenta un cambiamento paradigmatico nella gestione dello stato reattivo. Attraverso macro compile-time come $ref e $computed, gli sviluppatori possono eliminare la necessità di scrivere continuamente .value. Questa semplificazione sintattica produce codice più pulito e leggibile, ed è diventata un argomento sempre più frequente nei colloqui tecnici.
Reactivity Transform era sperimentale in Vue 3.2-3.3 ed è stato rimosso in Vue 3.4. Vue Macros o il plugin ufficiale @vue/reactivity-transform permettono ancora di utilizzare questa funzionalità. Nei colloqui, è importante conoscere il contesto storico e le alternative attuali.
Fondamenti di $ref e $computed
L'approccio classico della Composition API richiede accessi espliciti a .value per ogni lettura o scrittura di un valore reattivo. Il Reactivity Transform elimina questo boilerplate attraverso macro del compilatore che vengono trasformate in ref standard al momento della build.
// Approccio classico con ref()
import { ref, computed } from 'vue'
const count = ref(0)
const doubled = computed(() => count.value * 2)
function increment() {
count.value++
console.log(doubled.value)
}// Con Reactivity Transform
let count = $ref(0)
const doubled = $computed(() => count * 2)
function increment() {
count++
console.log(doubled)
}Il compilatore trasforma $ref(0) in ref(0) e inserisce automaticamente .value in tutti i punti dove count viene utilizzato. Il risultato è codice runtime identico con una migliore esperienza di sviluppo.
Comprensione Approfondita della Funzione di Escape $$()
La macro $$() risolve un problema critico: come passare una variabile reattiva come oggetto Ref senza che il compilatore la risolva in .value? Questa situazione si verifica frequentemente con composables e watchers.
import { watch } from 'vue'
let count = $ref(0)
// Errato: watch riceve il valore primitivo, non il Ref
watch(count, (newVal) => {
console.log(newVal)
})
// Corretto: $$() previene la trasformazione
watch($$(count), (newVal) => {
console.log(newVal)
})Senza $$(), il compilatore trasformerebbe count in count.value, passando a watch un valore primitivo invece di un riferimento reattivo. Il risultato sarebbe un watcher non funzionante.
Utilizzo con i Composables
I composables che accettano o restituiscono Refs richiedono particolare attenzione:
import { ref, Ref } from 'vue'
export function useCounter(initial: Ref<number>) {
const count = ref(initial.value)
function increment() {
count.value++
}
return { count, increment }
}
// Utilizzo nel Component
let initialValue = $ref(10)
// $$() passa l'oggetto Ref
const { count, increment } = useCounter($$(initialValue))$shallowRef e $customRef
Oltre a $ref, esistono varianti per casi d'uso specifici. $shallowRef crea un riferimento reattivo superficiale, ideale per oggetti di grandi dimensioni o strutture dati esterne:
interface LargeDataset {
items: Array<{ id: number; data: unknown }>
metadata: Record<string, unknown>
}
// Shallow Ref: Solo il riferimento stesso è reattivo
let dataset = $shallowRef<LargeDataset>({
items: [],
metadata: {}
})
// Trigger solo alla riassegnazione
dataset = { ...dataset, items: [...dataset.items, newItem] }
// Nessun trigger per modifiche profonde
dataset.items.push(newItem) // Vue non reagisce$customRef permette il controllo completo sul tracking e triggering:
function useDebouncedRef<T>(value: T, delay = 300) {
let timeout: ReturnType<typeof setTimeout>
return $customRef<T>((track, trigger) => ({
get() {
track()
return value
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
trigger()
}, delay)
}
}))
}
let searchQuery = useDebouncedRef('')
// Gli aggiornamenti vengono ritardati di 300ms
searchQuery = 'vue reactivity'Migrazione da ref() a $ref
La migrazione di codebase esistenti richiede un approccio sistematico. Il processo inizia con la configurazione del build tool:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [
vue({
script: {
defineModel: true,
propsDestructure: true
}
})
]
})Per progetti Vue 3.4+ attuali, è necessario il plugin esterno:
npm install -D @vue-macros/reactivity-transformimport ReactivityTransform from '@vue-macros/reactivity-transform'
export default defineConfig({
plugins: [
vue(),
ReactivityTransform()
]
})Migrazione Graduale
// Prima: Composition API Standard
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
const firstName = ref('')
const lastName = ref('')
const fullName = computed(() =>
`${firstName.value} ${lastName.value}`.trim()
)
watch(firstName, (newVal) => {
console.log('First name changed:', newVal)
})
function updateFirstName(value: string) {
firstName.value = value
}
</script>// Dopo: Con Reactivity Transform
<script setup lang="ts">
import { watch } from 'vue'
let firstName = $ref('')
let lastName = $ref('')
const fullName = $computed(() =>
`${firstName} ${lastName}`.trim()
)
watch($$(firstName), (newVal) => {
console.log('First name changed:', newVal)
})
function updateFirstName(value: string) {
firstName = value
}
</script>Integrazione TypeScript e Tipizzazione
Il supporto TypeScript richiede dichiarazioni di tipo globali. Le macro sono disponibili al compile-time e necessitano di definizioni appropriate:
/// <reference types="@vue-macros/reactivity-transform/macros-global" />I tipi generici funzionano in modo identico ai ref standard:
interface User {
id: number
name: string
email: string
}
let currentUser = $ref<User | null>(null)
let users = $ref<User[]>([])
const activeUsers = $computed<User[]>(() =>
users.filter(u => u.email.includes('@company.com'))
)
async function fetchUsers() {
const response = await fetch('/api/users')
users = await response.json()
}Props Destructure con Reactivity Transform
Vue 3.5 ha introdotto il Props Destructuring reattivo, che si integra perfettamente con il Reactivity Transform:
<script setup lang="ts">
interface Props {
initialCount?: number
title: string
disabled?: boolean
}
// Destructuring reattivo con valori di default
const {
initialCount = 0,
title,
disabled = false
} = defineProps<Props>()
// initialCount, title, disabled sono reattivi
let internalCount = $ref(initialCount)
const displayTitle = $computed(() =>
disabled ? `${title} (disabilitato)` : title
)
</script>Domande Frequenti nei Colloqui sulla Reattività Vue
I colloqui tecnici per posizioni Vue senior si concentrano sulla comprensione approfondita della reattività:
Domanda 1: Perché il Reactivity Transform è stato rimosso dal Core di Vue?
La decisione si è basata su diversi fattori: la trasformazione della sintassi introduceva comportamenti impliciti che causavano confusione durante le code review e il debugging. La distinzione tra variabili reattive e variabili normali non era più visivamente riconoscibile. Il team Vue ha deciso che gli accessi espliciti a .value, nonostante la verbosità, sono più manutenibili.
Domanda 2: Spiega la differenza tra ref() e reactive()
import { ref, reactive } from 'vue'
// ref: Wrapper per qualsiasi valore
const count = ref(0) // RefImpl { _value: 0 }
const user = ref({ name: '' }) // RefImpl { _value: { name: '' } }
// reactive: Proxy per oggetti
const state = reactive({ count: 0, user: { name: '' } })
// ref può contenere valori primitivi
// reactive solo oggetti
const primitive = reactive(0) // Warning: value cannot be made reactiveDomanda 3: Come funziona il Dependency Tracking di Vue?
import { ref, effect } from 'vue'
const count = ref(0)
// effect si registra come dependency
effect(() => {
// Leggendo count.value, l'effect viene registrato come subscriber
console.log(count.value)
})
// Alla modifica, tutti i subscriber vengono notificati
count.value++ // Triggera l'effectIl sistema di reattività di Vue utilizza Proxy (per reactive) e getter/setter (per ref) per tracciare gli accessi. Ad ogni lettura, l'effect corrente viene registrato come dependency; ad ogni scrittura, tutte le dependencies vengono notificate.
Domanda 4: Qual è la differenza tra $computed e computed?
import { computed } from 'vue'
// Standard computed: Restituisce ComputedRef
const doubled = computed(() => count.value * 2)
console.log(doubled.value) // Accesso con .value
// $computed: Risoluzione automatica di .value
const tripled = $computed(() => count * 3)
console.log(tripled) // Accesso direttoEntrambi producono codice runtime identico. La differenza risiede esclusivamente nell'esperienza di sviluppo e viene eliminata al momento della build.
Pronto a superare i tuoi colloqui su Vue.js / Nuxt.js?
Pratica con i nostri simulatori interattivi, flashcards e test tecnici.
Considerazioni sulle Performance
Il Reactivity Transform non ha overhead a runtime, poiché la trasformazione avviene al momento della build. Gli output generati sono identici al codice scritto manualmente:
// Source con $ref
let count = $ref(0)
count++
// Output compilato
import { ref } from 'vue'
const count = ref(0)
count.value++Tuttavia, le best practice per le performance devono essere rispettate:
// Da evitare: Nuovi oggetti nelle computed
const derived = $computed(() => {
return { ...largeObject, modified: true } // Nuovo oggetto ad ogni chiamata
})
// Meglio: Calcoli mirati
const isModified = $computed(() => largeObject.modified === true)Conclusioni e Raccomandazioni
Il Vue 3 Reactivity Transform offre una soluzione elegante alla verbosità della Composition API. Nonostante la rimozione dal core di Vue, la funzionalità rimane disponibile attraverso plugin esterni. Per i nuovi progetti, la scelta tra ref standard e Reactivity Transform dovrebbe essere presa consapevolmente.
Nei colloqui tecnici, una conoscenza approfondita di entrambi gli approcci dimostra comprensione delle decisioni architetturali di Vue. La conoscenza delle ragioni della rimozione dal core, insieme all'applicazione pratica con Vue Macros, mostra sia consapevolezza storica che competenza attuale.
Sapresti trovare il bug in Vue.js / Nuxt.js?
Uno snippet reale, un bug nascosto, un tentativo al giorno. Senza account per provare.

Scritto da
Anthony Fillion-MailletFondatore di SharpSkill
Sviluppatore fullstack da oltre 10 anni. Guida SharpSkill e risponde di tutto ciò che vi viene pubblicato.
Aggiornato il 9 settembre 2026
Tag
Condividi
Articoli correlati

Vue 3 Composition API: Guida completa alla reattività
Padroneggiare la Vue 3 Composition API con questa guida pratica. ref, reactive, computed, watch e composables per applicazioni Vue performanti.

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.

Vue 3 con TypeScript nel 2026: props, emits e composable type-safe
Padroneggiare componenti Vue 3 type-safe con TypeScript: defineProps generico, defineEmits a tupla, composable tipizzati, defineModel e InjectionKey, con domande da colloquio.