Dans Vue 2.x, la réactivité était implémentée via Object.defineProperty. Vue 3.x utilise désormais l'objet natif Proxy pour intercetper les opérations sur un objet.
// Objet source
const data = { name: 'js', age: 25 };
// Proxy pour intercepter get/set
const proxy = new Proxy(data, {
get(target, key) {
console.log('Lecture de la propriété', key);
return target[key];
},
set(target, key, value) {
target[key] = value;
console.log('Mise à jour de', key, 'avec', value);
// Retourner true pour signaler que la modification a réussi
return true;
}
});
console.log(proxy.name); // get → js
proxy.name = 'ts'; // set
shallowReactive ne rend réactif que le premier niveau de l'objet. shallowRef est une couche légère autour de shallowReactive en enveloppant la valeur dans un objet { value }.
function shallowRef(val) {
return shallowReactive({ value: val });
}
function shallowReactive(obj) {
return new Proxy(obj, {
get(target, key) {
return target[key];
},
set(target, key, value) {
target[key] = value;
console.log('Mise à jour de l’interface utilisateur');
return true;
}
});
}
const raw = {
a: 'a',
nested: {
b: 'b',
deep: { c: 'c', inner: { d: 'd' } }
}
};
// Exemple avec shallowReactive
const proxy1 = shallowReactive(raw);
proxy1.a = 1; // UI update affiché
proxy1.nested.b = 2; // Pas d'update (profondeur ignorée)
// Exemple avec shallowRef
const proxy2 = shallowRef(raw);
proxy2.value = { a: 'nouveau', nested: { b: 'nouveau' } }; // UI update déclenché
proxy2.value.nested.b = 'modif'; // Pas d'update
reactive rend récursivmeent un objet réactif en enveloppant chaque sous-objet dans un Proxy. ref utilise reactive derrière un conteneur { value }.
function ref(val) {
return reactive({ value: val });
}
function reactive(obj) {
if (typeof obj !== 'object' || obj === null) {
console.warn(`${JSON.stringify(obj)} n'est pas un objet`);
return obj;
}
// Traitement récursif des tableaux et objets
if (Array.isArray(obj)) {
obj.forEach((item, index) => {
if (typeof item === 'object' && item !== null) {
obj[index] = reactive(item);
}
});
} else {
for (const key in obj) {
const val = obj[key];
if (typeof val === 'object' && val !== null) {
obj[key] = reactive(val);
}
}
}
return new Proxy(obj, {
get(target, key) {
return target[key];
},
set(target, key, value) {
target[key] = value;
console.log('Mise à jour de l’interface utilisateur');
return true;
}
});
}
// Test avec un objet imbriqué
const data = {
a: 'a',
nested: {
b: 'b',
deep: { c: 'c', inner: { d: 'd' } }
}
};
const state = reactive(data);
state.a = 1; // UI update
state.nested.b = 2; // UI update
state.nested.deep.inner.d = 4; // UI update
// Test avec un tableau d'objets
const items = [{ id: 1, label: 'Alpha' }, { id: 2, label: 'Beta' }];
const arrState = reactive(items);
arrState[0].label = 'Gamma'; // UI update
arrState[0].id = 10; // UI update
readonly rend un objet récursivement en lecture seule, tandis que shallowReadonly ne bloque que le premier niveau.
function readonly(obj) {
if (typeof obj !== 'object' || obj === null) {
console.warn(`${JSON.stringify(obj)} n'est pas un objet`);
return obj;
}
if (Array.isArray(obj)) {
obj.forEach((item, index) => {
if (typeof item === 'object' && item !== null) {
obj[index] = readonly(item);
}
});
} else {
for (const key in obj) {
const val = obj[key];
if (typeof val === 'object' && val !== null) {
obj[key] = readonly(val);
}
}
}
return new Proxy(obj, {
get(target, key) {
return target[key];
},
set(target, key, value) {
console.warn(`La propriété "${key}" est en lecture seule, modification ignorée`);
return true;
}
});
}
function shallowReadonly(obj) {
return new Proxy(obj, {
get(target, key) {
return target[key];
},
set(target, key, value) {
console.warn(`La propriété "${key}" est en lecture seule (premier niveau)`);
return true;
}
});
}
const source = {
a: 'a',
nested: { b: 'b', deep: { c: 'c' } }
};
// shallowReadonly
const proxyRO = shallowReadonly(source);
proxyRO.a = 1; // Avertissement
proxyRO.nested.b = 2; // Modifié sans avertissement (profondeur)
// readonly récursif
const proxyFullRO = readonly(source);
proxyFullRO.a = 1; // Avertissement
proxyFullRO.nested.b = 2; // Avertissement également