API de composición de Vue 3: Ref o Reactiva





Ahora, mientras escribo este artículo, nos acercamos al lanzamiento de Vue 3. En mi opinión, lo más interesante es observar cómo lo percibirán y utilizarán otros desarrolladores. He tenido la oportunidad de jugar con Vue 3 en los últimos meses, pero sé que hay quienes no lo han hecho.



La innovación más significativa de la nueva versión es la API de composición. Proporciona un enfoque alternativo para crear componentes y es muy diferente de la API de opciones existente. No es difícil para mí admitir que cuando lo vi por primera vez, no lo entendí. Sin embargo, a medida que se aplicó, el significado comenzó a surgir. Es posible que no esté reescribiendo toda su aplicación utilizando la API de composición, pero este artículo le dará la oportunidad de pensar en cómo funcionará la creación y composición de componentes.







Recientemente hice un par de presentaciones y una pregunta común era cuándo uso Ref, y cuándo Reactive, para declarar una propiedad reactiva. No tuve una buena respuesta y me tomó un par de semanas encontrarla, y este artículo es el resultado de mi investigación.



También me gustaría señalar que lo anterior es mi opinión, y por favor no crea que es necesario hacerlo “solo de esta manera y no de otra manera”. Planeo usar Ref y Reactive de esta manera hasta que alguien me indique lo contrario, o hasta que encuentre un enfoque mejor yo mismo. Creo que se necesita algo de tiempo para comprender cualquier tecnología nueva, y luego pueden aparecer técnicas probadas.



Antes de continuar, supongo que, al menos, se familiarizará brevemente con la API de composición y comprenderá en qué consiste. Este artículo se centra en las diferencias entre ref y reactivo en lugar del mecanismo de composición de API.



Reactividad Vue 2



Para ponerte al día, echaré un vistazo rápido a cómo crear propiedades reactivas en una aplicación Vue 2. Cuando quieras que Vue 2 rastree los cambios en las propiedades, debes declararlos en el objeto devuelto por la función de datos.



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

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


Bajo el capó de Vue 2, se llama a Object.defineProperty () para cada propiedad para crear un getter y setter para realizar un seguimiento de los cambios. Esta es la explicación más simple del proceso y quiero transmitir la idea: no hay magia en ello. No puede crear propiedades reactivas en ningún lugar y esperar que Vue realice un seguimiento de los cambios en ellas. Necesita establecer propiedades reactivas en la función de datos.



REF y REACTIVO



Cuando trabajamos con la API de opciones, debemos seguir algunas reglas al declarar propiedades reactivas, al igual que con la API de composición. No puede simplemente crear una propiedad y esperar reactividad. En el siguiente ejemplo, he declarado la propiedad del título y la función setup () la devuelve, haciéndola disponible para la plantilla.



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

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




Esto funcionará, pero la propiedad del título no será reactiva. Aquellos. si alguien cambia el título, esos cambios NO se verán reflejados en la casa. Por ejemplo, si cambia el título después de 5 segundos, el código siguiente NO cambiará la casa.



<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>




Podemos importar ref y usarlo para hacer que la propiedad sea reactiva. Debajo del capó, Vue 3 creará 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>




Quiero aclarar que hablando de Ref y Reactive, creo que hay dos casos distintos. La primera es crear un componente como en el ejemplo anterior y necesita propiedades reactivas. El segundo es cuando crea funciones que permiten que la composición se utilice en componentes y funciones. Discutiremos ambos escenarios en este artículo.



ÁRBITRO



Al crear propiedades de tipos simples, ref () es su primera opción. No es una solución milagrosa, pero vale la pena comenzar. Permíteme recordarte siete tipos primitivos en JavaScript:



  • Cuerda
  • Número
  • Empezando
  • Booleano
  • Símbolo
  • Nulo
  • Indefinido




import { ref } from "vue";

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




En el ejemplo anterior, nuestro título es de tipo String, por lo que para hacer reactiva la propiedad, seleccionamos ref (). Si el código a continuación le está causando algunas preguntas, no se preocupe, yo tenía las mismas preguntas.



import { ref } from "vue";

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

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

    return { title };
  }
};




¿Por qué usamos const si el título cambiará? ¿Por qué no usar dejar? Si imprime el título en la consola, puede esperar ver Hello, Vue 3!, Pero en su lugar mostrará un objeto:



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




La función ref () tomará un argumento y devolverá un objeto ref reactivo y mutable. El objeto Ref tiene una propiedad, value, que se refiere al argumento. Esto significa que si desea obtener o cambiar el valor, tendrá que usar title.value, y como este es un objeto que no cambiará, lo declaré const.



Llamando a REF



La siguiente pregunta es ¿por qué no llamamos title.value en la plantilla?



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




Cuando Ref se devuelve como una propiedad en el contexto de representación (el objeto devuelto por la función setup ()) y se hace referencia a él en la plantilla, Ref devuelve automáticamente un valor. No es necesario agregar .value en la plantilla.



Las propiedades calculadas funcionan de la misma manera, dentro de la función setup (), consúltelas como .value.




REACTIVO



Solo vimos el uso de ref () para establecer la reactividad de las propiedades en tipos simples. ¿Y si queremos crear un objeto reactivo? Todavía podríamos usar ref (), pero bajo el capó Vue usará reactive (), así que me quedaré con reactive ().



Por otro lado, reactive () no funcionará con tipos primitivos. La función reactive () toma un objeto y devuelve el proxy reactivo del original. Esto es equivalente a .observable () en Vue 2, y este nombre de función se ha cambiado para evitar confusiones con observables en RxJS.



import { reactive } from "vue";

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

    return { data };
  }
};




La principal diferencia es cómo nos referimos al objeto reactivo en la plantilla. En el ejemplo anterior, los datos son un objeto que contiene una propiedad de título. Deberá consultarlo en la plantilla de esta manera: 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>




Diferencia entre REF y REACTIVO en un componente



Con base en lo que hemos discutido, la respuesta parecería sugerir por sí misma. Usamos ref () para tipos simples y reactive () para objetos. Cuando comencé a construir componentes, resultó que este no siempre es el caso, y la documentación dice:



La diferencia entre usar ref y reactive puede ser, hasta cierto punto, comparable a cómo se escribe la lógica de programa estándar en JavaScript.




Reflexioné sobre esta frase y llegué a las siguientes conclusiones. A medida que la aplicación creció, obtuve las siguientes propiedades:



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, entendería que todas estas son propiedades de mi página. En este caso, los agruparía en un objeto de página, ¿por qué no hacer lo mismo ahora?



<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>




Este es mi enfoque de Ref o Reactive, pero sería bueno tener su opinión. ¿Tú haces lo mismo? ¿Quizás este no es el enfoque correcto? Por favor comenta.



Lógica de composición



No puede equivocarse al usar ref () o reactivo () en sus componentes. Ambas funciones crearán datos reactivos, y si comprende cómo acceder a ellos en su función setup () y plantillas, no hay problema.



Cuando comience a escribir funciones de composición, debe comprender la diferencia. Utilizo ejemplos de la documentación de RFC, ya que algunos ilustran bien los matices.



Necesita crear lógica para rastrear la posición del mouse. También debería poder utilizar esta misma lógica en cualquier componente cuando sea necesario. Crea una función de composición que realiza un seguimiento de las coordenadas xey y luego las devuelve al código del cliente.



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 desea utilizar esta lógica en un componente y llamar a esta función, desestructura el objeto devuelto y luego devuelve las coordenadas xey a la plantilla.



<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>




Esto funcionará, pero si, después de mirar la función, decides refactorizar y devolver un objeto de posición en lugar de x e 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;
  }

  // ...
}




El problema con este enfoque es que el cliente de la función de composición siempre debe tener una referencia al objeto devuelto para que la reactividad persista. Esto significa que no podemos desestructurar el objeto o aplicar el operador de propagación:



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

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

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




Pero esto no significa que no podamos usar reactivo en este caso. Hay una función toRefs () que convierte un objeto reactivo en un objeto simple, cada propiedad del cual es la referencia de la propiedad correspondiente del objeto original.



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

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

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




Por lo tanto, hay algunos aspectos a considerar al crear funciones de composición. Si comprende cómo funciona el código del cliente con sus funciones, entonces todo estará bien.



TOTAL



Cuando comencé a usar la API de composición, me sorprendió la pregunta de cuándo aplicar ref () y cuándo reactive (). Quizás todavía no lo estoy haciendo bien, y hasta que alguien me diga esto, seguiré con este enfoque. Con suerte, ayudó a aclarar algunos de los problemas y me encantaría escuchar sus comentarios.



Codificación feliz



All Articles