Acabo de empezar a trabajar con Vue 3 y la API de composición.
Me preguntaba cuáles son las diferencias entre ref , toRef y toRefs .
ref vue 3 Un ref es un mecanismo de reactividad en Vue 3. La idea es envolver un no-objeto en un objeto reactive :
Toma un valor interno y devuelve un objeto ref reactivo y mutable. El objeto ref tiene una sola propiedad
.valueque apunta al valor interno.
¿Hmm por que?
En JavaScript (y muchos lenguajes OOP), hay 2 tipos de variables: valor y referencia .
Variables de valor: si una variable x contiene un valor como 10 , es una variable de valor . Si tuviera que copiar x a y , simplemente copia el valor. Cualquier cambio futuro en x no cambiará y .
Variables de referencia : pero si x contiene un objeto, entonces es una variable de referencia . Con estos, las propiedades de y cambian cuando cambian las propiedades de x , porque ambas se refieren al mismo objeto. (Prueba esto con Vanilla JavaScript si te sorprende).
Esto hace que las variables de referencia sean más dinámicas en cierto sentido y más útiles para la reactividad, ya que cualquier cambio puede reflejarse fácilmente en cualquier parte de la aplicación. Vue quiere aprovechar esta capacidad incluso para variables de valor simple, por lo que ref las envuelve en un objeto, lo que crea una variable de referencia.
reactivePara las variables de objeto, no se necesita un ajuste de referencia porque ya es un tipo de referencia. Solo necesita la funcionalidad reactiva de Vue (que también tiene un ref):
const state = reactive({ foo: 1, bar: 2 }) Pero las propiedades de este objeto pueden contener valores en lugar de referencias. Si tuviera que copiar una propiedad de valor directamente a otra ubicación, la copia perdería la conexión con el objeto y su reactividad. Aquí es donde toRef es útil.
toRef toRef convierte una sola propiedad de objeto reactive en una ref que mantiene su conexión con el objeto principal:
const state = reactive({ foo: 1, bar: 2 }) const fooRef = toRef(state, 'foo') /* fooRef: Ref<number>, */
toRefs toRefs convierte todas las propiedades en un objeto simple con propiedades que son referencias:
const state = reactive({ foo: 1, bar: 2 }) const stateAsRefs = toRefs(state) /* { foo: Ref<number>, bar: Ref<number> } */
reactivo
reactive crea un objeto proxy profundamente reactivo basado en un objeto dado. El objeto proxy se verá exactamente igual que el objeto simple dado, pero cualquier mutación, sin importar qué tan profunda sea, será reactiva; esto incluye todo tipo de mutaciones, incluidas las adiciones y eliminaciones de propiedades. Lo importante es que reactive solo puede funcionar con objetos, no con primitivos.
Por ejemplo, const state = reactive({foo: {bar: 1}}) significa:
state.foo es reactivo (se puede usar en plantilla, computar y observar)state.foo.bar es reactivostate.baz , state.foo.baz , state.foo.bar.baz también son reactivos aunque baz aún no existe en ninguna parte. Esto puede parecer sorprendente (especialmente cuando comienzas a investigar cómo funciona la reactividad en vue). Cuando state.baz es reactivo , me refiero a que dentro de su plantilla/propiedades computadas/relojes, puede escribir state.baz literalmente y esperar que su lógica se ejecute nuevamente cuando state.baz esté disponible . De hecho, incluso si escribes algo como {{ state.baz ? state.baz.qux : "default value" }} en su plantilla, también funcionará. La cadena final que se muestra reflejará reactivamente state.baz.qux. Esto puede suceder porque reactive no solo crea un solo objeto proxy de nivel superior, sino que también convierte recursivamente todos los objetos anidados en proxies reactivos, y este proceso continúa en tiempo de ejecución incluso para los subobjetos creados sobre la marcha. Las dependencias de las propiedades de los objetos reactivos se descubren y rastrean continuamente en tiempo de ejecución cada vez que se realiza un intento de acceso a la propiedad contra un objeto reactivo . Con esto en mente, puedes resolver esta expresión {{ state.baz ? state.baz.qux : "default value" }} paso a paso:
state para la propiedad baz ). Al ser un objeto proxy, state recordará que su expresión depende de su propiedad baz , aunque baz aún no existe. La reactividad de baz la proporciona el objeto de state que posee la propiedad .state.baz devuelve undefined , la expresión se evalúa como "valor predeterminado" sin molestarse en mirar state.baz.qux . No hay ninguna dependencia registrada en state.baz.qux en esta ronda, pero está bien. Porque no puedes mutar qux sin mutar baz primero .state.baz : state.baz = { qux: "hello" } . Esta mutación califica como una mutación de la propiedad baz de state , por lo tanto, su expresión está programada para una reevaluación. Mientras tanto, lo que se asigna a state.baz es un subproxy creado sobre la marcha para { qux: "hello" }state.baz no está undefined , por lo que la expresión avanza a state.baz.qux . Se devuelve "hola" y se registra una dependencia en la propiedad qux del objeto proxy state.baz . Esto es lo que quiero decir con que las dependencias se descubren y registran en tiempo de ejecución a medida que ocurren .state.baz.qux = "hi" . Esta es una mutación de la propiedad qux y, por lo tanto, su expresión se evaluará nuevamente. Con lo anterior en mente, también debería poder entender esto: puede almacenar state.foo en una variable separada: const foo = state.foo . La reactividad funciona bien con tu variable foo . foo apunta a lo mismo que state.foo apunta: un objeto proxy reactivo. El poder de la reactividad proviene del objeto proxy. Por cierto, const baz = state.baz no funcionaría igual, más sobre esto más adelante.
Sin embargo, siempre hay casos extremos a tener en cuenta:
baz creada por const baz = state.baz , ni con la variable bar de const bar = state.foo.bar . Para que quede claro, lo que significa es que puede usar state.baz y state.foo.bar en su plantilla/computado/reloj, pero no baz o bar creado anteriormente.state.foo = {bar: 3} ) no destruye la reactividad de foo , pero state.foo será un nuevo objeto proxy mientras que la variable foo todavía apunta al objeto proxy original. const state = reactive({foo: {bar: 1}}); const foo = state.foo; state.foo.bar = 2; foo.bar === 2; // true, because foo and state.foo are the same state.foo = {bar: 3}; foo.bar === 3; // false, foo.bar will still be 2 ref y toRef resuelven algunos de estos casos extremos.
árbitro
ref es más o menos el reactive que funciona también con primitivas. Todavía no podemos convertir las primitivas JS en objetos Proxy, por lo que ref siempre envuelve el argumento proporcionado X en un objeto de forma {value: X} . No importa si X es primitivo o no, el "boxeo" siempre ocurre. Si se le da un objeto a ref , ref llama internamente a reactive después del boxeo, por lo que el resultado también es profundamente reactivo. La principal diferencia en la práctica es que debe tener en cuenta llamar a .value en su código js cuando trabaje con ref. En su plantilla, no tiene que llamar a .value porque Vue desenvuelve automáticamente la referencia en la plantilla.
const count = ref(1); const objCount = ref({count: 1}); count.value === 1; // true objCount.value.count === 1; // trueaRef
toRef está destinado a convertir una propiedad de un objeto reactivo en una ref . Quizás se pregunte por qué esto es necesario, ya que el objeto reactivo ya es profundamente reactivo. toRef está aquí para manejar los dos casos extremos mencionados para reactive . En resumen, toRef puede convertir cualquier propiedad de un objeto reactivo en una referencia vinculada a su padre original. La propiedad puede ser una que no exista inicialmente, o cuyo valor sea primitivo.
En el mismo ejemplo donde el estado se define como const state = reactive({foo: {bar: 1}}) :
const foo = toRef(state, 'foo') será muy similar a const foo = state.foo pero con dos diferencias:foo es una ref , por lo que debe hacer foo.value en js;foo está vinculado a su padre, por lo que reasignar state.foo = {bar: 2} se reflejará en foo.valueconst baz = toRef(state, 'baz') ahora funciona.aRefs
toRefs es un método de utilidad utilizado para destruir un objeto reactivo y convertir todas sus propiedades en ref:
const state = reactive({...}); return {...state}; // will not work, destruction removes reactivity return toRefs(state); // works