API de composition Vue 3: Ref ou Reactive





Maintenant, au moment où j'écris cet article, nous nous rapprochons de la sortie de Vue 3. À mon avis, le plus intéressant est d'observer comment les autres développeurs le percevront et l'utiliseront. J'ai eu l'occasion de jouer avec Vue 3 ces derniers mois, mais je sais qu'il y en a qui ne l'ont pas fait.



L'innovation la plus importante de la nouvelle version est l'API de composition. Il offre une approche alternative à la création de composants et est très différent de l'API Options existante. Il n'est pas difficile pour moi d'admettre que la première fois que je l'ai vu, je ne l'ai pas compris. Cependant, au fur et à mesure de son application, le sens a commencé à émerger. Vous ne réécrivez peut-être pas l'intégralité de votre application à l'aide de l'API de composition, mais cet article vous donnera l'occasion de réfléchir au fonctionnement de la création et de la composition de composants.







J'ai fait quelques présentations récemment, et une question courante était quand j'utilise Ref, et quand Reactive, pour déclarer une propriété réactive. Je n'avais pas de bonne réponse et j'ai mis quelques semaines à trouver la réponse, et cet article est le résultat de mes recherches.



Je voudrais également noter que ce qui précède est mon opinion, et je vous prie de ne pas penser qu'il est nécessaire de faire «seulement de cette façon et pas autrement». Je prévois d'utiliser Ref et Reactive de cette façon jusqu'à ce que quelqu'un conseille le contraire, ou jusqu'à ce que je trouve moi-même une meilleure approche. Je pense qu'il faut du temps pour comprendre toute nouvelle technologie, puis des techniques éprouvées peuvent apparaître.



Avant de continuer, je suppose que vous vous familiarisez au moins brièvement avec l'API de composition et que vous comprenez en quoi elle consiste. Cet article se concentre sur les différences entre ref et réactif plutôt que sur le mécanisme de composition de l'API.



Réactivité Vue 2



Pour vous mettre à jour, je vais jeter un coup d'œil sur la façon de créer des propriétés réactives dans une application Vue 2. Lorsque vous voulez que Vue 2 suive les changements de propriétés, vous devez les déclarer dans l'objet renvoyé par la fonction de données.



<template>
  <h1>{{ title }}</h1>
</template>

<script>
  export default {
    data() {
      return {
        title: "Hello, Vue!"
      };
    }
  };
</script>


Sous le capot de Vue 2, Object.defineProperty () est appelé pour chaque propriété pour créer un getter et un setter pour suivre les changements. C'est l'explication la plus simple du processus et je veux transmettre l'idée: il n'y a pas de magie dedans. Vous ne pouvez pas créer de propriétés réactives nulle part et vous attendre à ce que Vue en suive les modifications. Vous devez définir des propriétés réactives dans la fonction de données.



REF et REACTIF



Lorsque vous travaillez avec l'API Options, nous devons suivre certaines règles lors de la déclaration de propriétés réactives, et il en va de même lorsque vous travaillez avec l'API Composition. Vous ne pouvez pas simplement créer une propriété et vous attendre à de la réactivité. Dans l'exemple suivant, j'ai déclaré la propriété title et la fonction setup () la renvoie, la rendant ainsi disponible pour le modèle.



<template>
  <h1>{{ title }}</h1>
</template>

<script>
  export default {
    setup() {
      let title = "Hello, Vue 3!";
      return { title };
    }
  };
</script>




Cela fonctionnera, mais la propriété title ne sera pas réactive. Ceux. si quelqu'un change le titre, ces changements ne seront PAS reflétés à la Chambre. Par exemple, si vous changez le titre après 5 secondes, le code ci-dessous ne changera PAS la maison.



<template>
  <h1>{{ title }}</h1>
</template>

<script>
  export default {
    setup() {
      let title = "Hello, Vue 3!";

      setTimeout(() => {
        title = "THIS IS A NEW TITLE";
      }, 5000);

      return { title };
    }
  };
</script>




Nous pouvons importer ref et l'utiliser pour rendre la propriété réactive. Sous le capot, Vue 3 créera un proxy .



<template>
  <h1>{{ title }}</h1>
</template>

<script>
  import { ref } from "vue";

  export default {
    setup() {
      const title = ref("Hello, Vue 3!");

      setTimeout(() => {
        //   ,   .value ...
        //   
        title.value = "New Title";
      }, 5000);

      return { title };
    }
  };
</script>




Je tiens à préciser qu'en parlant de Ref et Reactive, je pense qu'il y a deux cas différents. La première consiste à créer un composant comme dans l'exemple ci-dessus et vous avez besoin de propriétés réactives. Le second est lorsque vous créez des fonctions qui permettent d'utiliser la composition dans des composants et des fonctions. Nous discuterons des deux scénarios dans cet article.



REF



Lors de la création de propriétés de types simples, ref () est votre premier choix. Ce n'est pas une solution miracle, mais cela vaut la peine de commencer. Permettez-moi de vous rappeler sept types primitifs en JavaScript:



  • Chaîne
  • Nombre
  • BigInt
  • Booléen
  • symbole
  • Nul
  • Indéfini




import { ref } from "vue";

export default {
  setup() {
    const title = ref("");
    const one = ref(1);
    const isValid = ref(true);
    const foo = ref(null);
  }
};




Dans l'exemple précédent, notre titre est de type String, donc pour rendre la propriété réactive, nous sélectionnons ref (). Si le code ci-dessous vous pose des questions, ne vous inquiétez pas, j'avais les mêmes questions.



import { ref } from "vue";

export default {
  setup() {
    const title = ref("Hello, Vue 3!");

    setTimeout(() => {
      title.value = "New Title";
    }, 5000);

    return { title };
  }
};




Pourquoi utilisons-nous const si le titre change? Pourquoi ne pas utiliser let? Si vous imprimez le titre sur la console, vous pourriez vous attendre à voir Hello, Vue 3!, Mais à la place, il affichera un objet:



{_isRef: true}
value: (...)
_isRef: true
get value: ƒ value()
set value: ƒ value(newVal)
__proto__: Object




La fonction ref () prendra un argument et retournera un objet ref réactif et mutable. L'objet Ref a une propriété, valeur, qui fait référence à l'argument. Cela signifie que si vous voulez obtenir ou modifier la valeur, vous devrez utiliser title.value, et comme il s'agit d'un objet qui ne changera pas, je l'ai déclaré const.



Appel REF



La question suivante est pourquoi nous n'appelons pas title.value dans le modèle?



<template>
  <h1>{{ title }}</h1>
</template>




Lorsque Ref est retourné en tant que propriété dans le contexte de rendu (l'objet retourné par la fonction setup ()) et est référencé dans le modèle, Ref renvoie automatiquement la valeur. Vous n'avez pas besoin d'ajouter .value dans le modèle.



Les propriétés calculées fonctionnent de la même manière, dans la fonction setup (), appelez-les .value.




RÉACTIF



Nous venons de voir comment ref () peut être utilisé pour rendre réactives les propriétés de types simples. Et si nous voulons créer un objet réactif? Nous pourrions toujours utiliser ref (), mais sous le capot, Vue utilisera reactive (), donc je m'en tiendrai à reactive ().



D'autre part, reactive () ne fonctionnera pas avec les types primitifs. La fonction reactive () prend un objet et renvoie le proxy réactif de l'original. Ceci est équivalent à .observable () dans Vue 2, et ce nom de fonction a été modifié pour éviter toute confusion avec les observables dans RxJS.



import { reactive } from "vue";

export default {
  setup() {
    const data = reactive({
      title: "Hello, Vue 3"
    });

    return { data };
  }
};




La principale différence est la façon dont nous faisons référence à l'objet réactif dans le modèle. Dans l'exemple précédent, data est un objet contenant une propriété title. Vous devrez vous y référer dans le modèle comme ceci - data.title:



<template>
  <h1>{{ data.title }}</h1>
</template>

<script>
  import { ref } from "vue";

  export default {
    setup() {
      const data = ref({
        title: "Hello, Vue 3"
      });

      return { data };
    }
  };
</script>




Différence entre REF et REACTIVE dans un composant



Sur la base de ce dont nous avons discuté, la réponse semble se suggérer? Nous utilisons ref () pour les types simples et reactive () pour les objets. Quand j'ai commencé à construire des composants, il s'est avéré que ce n'était toujours pas le cas, et la documentation dit:



La différence entre l'utilisation de ref et de réactif peut être, dans une certaine mesure, comparable à la façon dont vous écrivez une logique de programme standard en JavaScript.




J'ai réfléchi à cette phrase et suis arrivé aux conclusions suivantes. Au fur et à mesure que l'application grandissait, j'ai obtenu les propriétés suivantes:



export default {
  setup() {
    const title = ref("Hello, World!");
    const description = ref("");
    const content = ref("Hello world");
    const wordCount = computed(() => content.value.length);

    return { title, description, content, wordCount };
  }
};




En JavaScript, je comprendrais que ce sont toutes les propriétés de ma page. Dans ce cas, je les regrouperais dans un objet de page, pourquoi ne pas faire de même maintenant.



<template>
  <div class="page">
    <h1>{{ page.title }}</h1>
    <p>{{ page.wordCount }}</p>
  </div>
</template>

<script>
  import { ref, computed, reactive } from "vue";

  export default {
    setup() {
      const page = reactive({
        title: "Hello, World!",
        description: "",
        content: "Hello world",
        wordCount: computed(() => page.content.length)
      });

      return { page };
    }
  };
</script>




C'est mon approche de Ref ou Reactive, mais ce serait bien d'avoir votre avis. Faites-vous la même chose? Ce n'est peut-être pas la bonne approche? Commentez s'il vous plaît.



Logique de composition



Vous ne pouvez pas vous tromper lorsque vous utilisez ref () ou reactive () dans vos composants. Les deux fonctions créeront des données réactives, et si vous comprenez comment y accéder dans votre fonction setup () et vos modèles, il n'y a pas de problème.



Lorsque vous commencez à écrire des fonctions de composition, vous devez comprendre la différence. J'utilise des exemples tirés de la documentation RFC, car certains illustrent bien les nuances.



Vous devez créer une logique pour suivre la position de la souris. Vous devriez également pouvoir utiliser cette même logique dans n'importe quel composant si nécessaire. Vous créez une fonction de composition qui assure le suivi des coordonnées x et y, puis les renvoie au code client.



import { ref, onMounted, onUnmounted } from "vue";

export function useMousePosition() {
  const x = ref(0);
  const y = ref(0);

  function update(e) {
    x.value = e.pageX;
    y.value = e.pageY;
  }

  onMounted(() => {
    window.addEventListener("mousemove", update);
  });

  onUnmounted(() => {
    window.removeEventListener("mousemove", update);
  });

  return { x, y };
}




Si vous souhaitez utiliser cette logique dans un composant et appeler cette fonction, déstructurez l'objet retourné, puis renvoyez les coordonnées x et y au modèle.



<template>
  <h1>Use Mouse Demo</h1>
  <p>x: {{ x }} | y: {{ y }}</p>
</template>

<script>
  import { useMousePosition } from "./use/useMousePosition";

  export default {
    setup() {
      const { x, y } = useMousePosition();
      return { x, y };
    }
  };
</script>




Cela fonctionnera, mais si, après avoir examiné la fonction, vous décidez de refactoriser et de renvoyer un objet position au lieu de x et y:



import { ref, onMounted, onUnmounted } from "vue";

export function useMousePosition() {
  const pos = {
    x: 0,
    y: 0
  };

  function update(e) {
    pos.x = e.pageX;
    pos.y = e.pageY;
  }

  // ...
}




Le problème avec cette approche est que le client de la fonction de composition doit toujours avoir une référence à l'objet retourné pour que la réactivité persiste. Cela signifie que nous ne pouvons pas déstructurer l'objet ni appliquer l'opérateur d'étalement:



//  
export default {
  setup() {
    //  !
    const { x, y } = useMousePosition();
    return {
      x,
      y
    };

    //  !
    return {
      ...useMousePosition()
    };

    //     
    //   `pos`  ,      x  y : `pos.x`  `pos.y`
    return {
      pos: useMousePosition()
    };
  }
};




Mais cela ne signifie pas que nous ne pouvons pas utiliser de réactif dans ce cas. Il existe une fonction toRefs () qui convertit un objet réactif en un objet simple, dont chaque propriété est la référence de la propriété correspondante de l'objet d'origine.



function useMousePosition() {
  const pos = reactive({
    x: 0,
    y: 0
  });

  // ...
  return toRefs(pos);
}

// x & y  ref!
const { x, y } = useMousePosition();




Ainsi, certains aspects doivent être pris en compte lors de la création de fonctions de composition. Si vous comprenez comment le code client fonctionne avec vos fonctions, tout ira bien.



TOTAL



Quand j'ai commencé à utiliser l'API Composition, j'étais perplexe à la question de savoir quand appliquer ref () et quand réactiver (). Peut-être que je ne fais toujours pas les choses correctement, et jusqu'à ce que quelqu'un me dise cela, je m'en tiendrai à cette approche. J'espère avoir aidé à clarifier certains des problèmes et nous serions ravis d'entendre vos commentaires.



Bon codage



All Articles