Implémentation manuelle des API de Vue 3.0

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

Étiquettes: Vue3 Proxy reactive ref shallowReactive

Publié le 24 juin à 02h19