Vai al contenuto principale

[Hard] 📄 Two-way Data Binding (Binding Bidirezionale dei Dati)

1. Spiega il principio sottostante di come Vue2 e Vue3 implementano il binding bidirezionale

Spiega il meccanismo sottostante del binding bidirezionale in Vue2 e Vue3.

Per comprendere il binding bidirezionale di Vue, è necessario capire come funziona il sistema di reattività e come Vue2 e Vue3 lo implementano in modo diverso.

Implementazione di Vue2

Vue2 utilizza Object.defineProperty per implementare la reattività. Avvolge le proprietà degli oggetti con getter e setter, in modo che letture/scritture possano essere tracciate.

1. Data hijacking (intercettazione dei dati)

Quando i dati del componente vengono inizializzati in Vue2, Vue attraversa ogni proprietà e la converte in getter/setter tramite Object.defineProperty, in modo che letture e scritture possano essere osservate.

2. Raccolta delle dipendenze

Quando una funzione di render viene eseguita e legge proprietà reattive, il getter viene eseguito. Vue registra le dipendenze in modo che i componenti interessati possano essere notificati successivamente.

3. Invio degli aggiornamenti

Quando i dati cambiano, il setter viene eseguito. Vue notifica i watcher/componenti dipendenti e attiva il re-render per aggiornare il DOM.

Esempio Vue2

function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
console.log(`get ${key}: ${val}`);
return val;
},
set: function reactiveSetter(newVal) {
console.log(`set ${key}: ${newVal}`);
val = newVal;
},
});
}

const data = { name: 'Pitt' };
defineReactive(data, 'name', data.name);

console.log(data.name); // attiva il getter
data.name = 'Vue2 Reactivity'; // attiva il setter

Limitazioni di Vue2

Object.defineProperty ha diverse limitazioni:

  • Non riesce a rilevare l'aggiunta/eliminazione di proprietà senza Vue.set() / Vue.delete()
  • Non riesce a rilevare in modo affidabile la mutazione diretta dell'indice dell'array senza metodi array patchati
  • Sovraccarico prestazionale dal deep traversal e dalla definizione anticipata di getter/setter

Implementazione di Vue3

Vue3 utilizza Proxy di ES6, che fornisce una capacità di intercettazione più ampia e migliori caratteristiche prestazionali.

1. Data hijacking basato su Proxy

Vue3 avvolge interi oggetti con new Proxy invece di definire getter/setter per ogni chiave. Questo permette l'intercettazione di più operazioni, inclusa l'aggiunta/eliminazione di proprietà.

2. Tracciamento delle dipendenze più efficiente

Vue3 non necessita più della definizione anticipata di getter/setter per ogni proprietà. Le trap del Proxy possono gestire le operazioni dinamicamente (es. get, set, has, deleteProperty, ecc.).

3. Attivazione degli aggiornamenti più precisa

Vue3 può tracciare le dipendenze con maggiore precisione e ridurre i re-render non necessari.

Esempio Vue3

function reactive(target) {
const handler = {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver);
console.log(`get ${key}: ${result}`);
return result;
},
set(target, key, value, receiver) {
const success = Reflect.set(target, key, value, receiver);
console.log(`set ${key}: ${value}`);
return success;
},
};

return new Proxy(target, handler);
}

const data = reactive({ name: 'Vue 3' });

console.log(data.name);
data.name = 'Vue 3 Reactivity';
console.log(data.name);

Confronto Vue2 vs Vue3

CaratteristicaVue2Vue3
Meccanismo principaleObject.definePropertyProxy
Rilevamento nuove proprietà❌ richiede Vue.set()✅ supporto nativo
Rilevamento eliminazione proprietà❌ richiede Vue.delete()✅ supporto nativo
Rilevamento assegnazione indice array❌ limitato✅ supporto nativo
Prestazionideep walk anticipatolazy + più efficiente
Supporto browserIE9+nessun supporto IE11

Conclusione

Vue2 utilizza Object.defineProperty, che funziona ma ha limitazioni note. Vue3 utilizza Proxy, offrendo un sistema di reattività più completo e flessibile con migliori prestazioni.

2. Perché Vue3 usa Proxy invece di Object.defineProperty?

Perché Vue3 ha scelto Proxy rispetto a Object.defineProperty?

Motivazioni principali

1. Capacità di intercettazione più forte

Proxy può intercettare molte operazioni, mentre Object.defineProperty copre solo get/set delle proprietà.

// Operazioni che Proxy può intercettare
const handler = {
get() {},
set() {},
has() {},
deleteProperty() {},
ownKeys() {},
getOwnPropertyDescriptor() {},
defineProperty() {},
preventExtensions() {},
getPrototypeOf() {},
isExtensible() {},
setPrototypeOf() {},
apply() {},
construct() {},
};

2. Tracciamento nativo degli indici array

// Limitazione di Vue2
const arr = [1, 2, 3];
arr[0] = 10; // spesso non tracciato nel modello defineProperty stile Vue2

// Vue3
const arr2 = reactive([1, 2, 3]);
arr2[0] = 10; // tracciato

3. Supporto nativo per aggiunta/eliminazione dinamica di proprietà

// Vue2 necessita di API speciali
Vue.set(obj, 'newKey', 'value');

// Vue3 nativo
const obj = reactive({});
obj.newKey = 'value';
delete obj.newKey;

4. Modello prestazionale migliore

// Vue2: deep traversal + defineReactive per ogni chiave
function observe(obj) {
Object.keys(obj).forEach((key) => {
defineReactive(obj, key, obj[key]);
if (typeof obj[key] === 'object') {
observe(obj[key]);
}
});
}

// Vue3: wrapper proxy
function reactive(obj) {
return new Proxy(obj, handler);
}

5. Implementazione interna più semplice

Il core della reattività di Vue3 è più pulito e più facile da mantenere.

Perché Vue2 non ha usato Proxy

Principalmente per compatibilità browser:

  • Vue2 (rilasciato nel 2016) necessitava di ampio supporto incluso IE
  • Proxy non può essere completamente polyfillato
  • Vue3 ha abbandonato il supporto IE11, rendendo Proxy utilizzabile

Confronto pratico

// ===== Limitazioni Vue2 =====
const vm = new Vue({
data: {
obj: { a: 1 },
arr: [1, 2, 3],
},
});

// non reattivo in modo affidabile in Vue2 senza API speciali
vm.obj.b = 2;
delete vm.obj.a;
vm.arr[0] = 10;
vm.arr.length = 0;

// Workaround Vue2
Vue.set(vm.obj, 'b', 2);
Vue.delete(vm.obj, 'a');
vm.arr.splice(0, 1, 10);

// ===== Supporto nativo Vue3 =====
const state = reactive({
obj: { a: 1 },
arr: [1, 2, 3],
});

state.obj.b = 2;
delete state.obj.a;
state.arr[0] = 10;
state.arr.length = 0;

Vue3 utilizza Proxy per:

  1. ✅ Fornire copertura di reattività completa (aggiunta/eliminazione/modifiche indice)
  2. ✅ Migliorare le prestazioni (meno traversal anticipato)
  3. ✅ Semplificare l'implementazione
  4. ✅ Migliorare l'esperienza sviluppatore (meno API speciali)

Compromesso: nessun supporto per IE11 legacy, che è accettabile per target web moderni.

Riferimenti