03. Reactividad
Sistema de Reactividad en Vue 3
Section titled “Sistema de Reactividad en Vue 3”El sistema de reactividad de Vue 3 ha sido completamente reescrito utilizando las capacidades modernas de JavaScript, especialmente los Proxies de ES6. Esto permite un seguimiento de dependencias más preciso y eficiente que en Vue 2.
La función ref() crea un objeto reactivo para valores primitivos (string, number, boolean) y objetos. Envuelve el valor en un objeto con una propiedad .value.
import { ref } from 'vue'
// Para valores primitivosconst count = ref(0)const name = ref('Vue 3')const isActive = ref(true)
// Para objetosconst user = ref({ id: 1, name: 'John' })
// Acceso y modificaciónconsole.log(count.value) // 0count.value++ // 1
// Con objetos, la propiedad sigue siendo reactivaconsole.log(user.value.name) // 'John'user.value.name = 'Jane' // Reactivo
// También se puede reemplazar todo el objetouser.value = { id: 2, name: 'Alice' } // Reactivo¿Cuándo usar ref()?
Section titled “¿Cuándo usar ref()?”- Para valores primitivos (siempre)
- Para objetos cuando necesitas reemplazar toda la referencia
- Cuando necesitas pasar la referencia reactiva a funciones
reactive()
Section titled “reactive()”La función reactive() convierte un objeto en un proxy reactivo. A diferencia de ref(), no requiere acceder a una propiedad .value, pero solo funciona con objetos y arrays.
import { reactive } from 'vue'
// Crear un objeto reactivoconst state = reactive({count: 0,user: { name: 'John', age: 30},items: ['apple', 'banana']})
// Acceso directo (sin .value)console.log(state.count) // 0state.count++ // 1
// Las propiedades anidadas también son reactivasstate.user.age++ // 31state.items.push('orange') // ReactivoLimitaciones de reactive()
Section titled “Limitaciones de reactive()”- Solo funciona con objetos: No puede hacer reactivos valores primitivos
- Pierde reactividad con desestructuración:
const state = reactive({ count: 0 })
// Esto rompe la reactividadconst { count } = state// 'count' ya no es reactivo- No se puede reasignar la variable:
let state = reactive({ count: 0 })
// Esto rompe la reactividadstate = reactive({ count: 1 })¿Cuándo usar reactive()?
Section titled “¿Cuándo usar reactive()?”- Para objetos complejos con muchas propiedades anidadas
- Cuando prefieres una sintaxis más limpia sin
.value - Cuando no necesitas reemplazar toda la referencia
readonly()
Section titled “readonly()”La función readonly() crea una versión de solo lectura de un objeto reactivo (creado con ref() o reactive()).
import { ref, reactive, readonly } from 'vue'
// Con refconst original = ref({ count: 0 })const copy = readonly(original)
// Con reactiveconst state = reactive({ count: 0 })const readonlyState = readonly(state)
// Intentar modificar genera una advertenciacopy.value.count++ // Advertencia en consolareadonlyState.count++ // Advertencia en consola
// El original sigue siendo modificableoriginal.value.count++ // Funciona y actualiza copyCasos de uso para readonly()
Section titled “Casos de uso para readonly()”- Pasar datos a componentes hijos que no deben modificarlos
- Crear una copia inmutable de un estado para prevenir modificaciones accidentales
- Implementar patrones de flujo de datos unidireccional
shallowRef() y shallowReactive()
Section titled “shallowRef() y shallowReactive()”Estas versiones “superficiales” de ref() y reactive() solo hacen reactivo el nivel superior del objeto, no sus propiedades anidadas.
import { shallowRef } from 'vue'
const state = shallowRef({ count: 0, user: { name: 'John' } })
// Esto es reactivostate.value = { count: 1, user: { name: 'Jane' } }
// Esto NO es reactivo (no desencadena actualizaciones)state.value.count++state.value.user.name = 'Jane'import { shallowReactive } from 'vue'
const state = shallowReactive({ count: 0, user: { name: 'John' } })
// Esto es reactivostate.count++
// Esto NO es reactivo (no desencadena actualizaciones)state.user.name = 'Jane'¿Cuándo usar las versiones shallow?
Section titled “¿Cuándo usar las versiones shallow?”- Para mejorar el rendimiento con objetos grandes y complejos
- Cuando solo necesitas reactividad en el primer nivel
- Para objetos que contienen datos que no deben ser reactivos (como bibliotecas externas)
toRef() y toRefs()
Section titled “toRef() y toRefs()”Estas funciones ayudan a mantener la reactividad cuando necesitas desestructurar propiedades de objetos reactivos.
toRef()
Section titled “toRef()”Crea una referencia reactiva a una propiedad específica de un objeto reactivo.
import { reactive, toRef } from 'vue'
const state = reactive({ count: 0, name: 'Vue' })
// Crear una referencia a state.countconst count = toRef(state, 'count')
// Ambas formas actualizan el estado y son reactivascount.value++state.count++ // Ambos son 2toRefs()
Section titled “toRefs()”Convierte un objeto reactivo en un objeto normal donde cada propiedad es una referencia reactiva.
import { reactive, toRefs } from 'vue'
const state = reactive({ count: 0, name: 'Vue' })
// Convertir todas las propiedades a refsconst stateRefs = toRefs(state)
// Ahora podemos desestructurar manteniendo la reactividadconst { count, name } = stateRefs
// Ambas formas actualizan el estado y son reactivascount.value++state.count++ // Ambos son 2Caso de uso común: retornar estado de composables
Section titled “Caso de uso común: retornar estado de composables”function useFeature() { const state = reactive({ count: 0, name: 'Feature' })
function increment() { state.count++ }
// Permite desestructurar manteniendo reactividad return { ...toRefs(state), increment }}
// En un componenteconst { count, name, increment } = useFeature()unref()
Section titled “unref()”La función unref() extrae el valor interno de un ref, o devuelve el argumento tal cual si no es un ref.
import { ref, unref } from 'vue'
const count = ref(0)const plainNumber = 1
console.log(unref(count)) // 0 (extrae el valor)console.log(unref(plainNumber)) // 1 (devuelve tal cual)Es equivalente a: isRef(val) ? val.value : val
Casos de uso para unref()
Section titled “Casos de uso para unref()”- Funciones que aceptan tanto refs como valores normales
- Simplificar código que maneja diferentes tipos de entrada
Funciones de verificación: isRef(), isReactive(), isReadonly()
Section titled “Funciones de verificación: isRef(), isReactive(), isReadonly()”Estas funciones permiten comprobar el tipo de un valor reactivo.
import { ref, reactive, readonly, isRef, isReactive, isReadonly } from 'vue'
const num = ref(0)const obj = reactive({count: 0})const readonlyObj = readonly(obj)const plainObj = { count: 0 }
// Verificacionesconsole.log(isRef(num)) // trueconsole.log(isRef(plainObj)) // false
console.log(isReactive(obj)) // trueconsole.log(isReactive(num)) // falseconsole.log(isReactive(num.value)) // falseconsole.log(isReactive(plainObj)) // false
console.log(isReadonly(readonlyObj)) // trueconsole.log(isReadonly(obj)) // falseCasos de uso
Section titled “Casos de uso”- Funciones que necesitan comportarse de manera diferente según el tipo de entrada
- Depuración y verificación de tipos
- Implementación de lógica condicional basada en el tipo de reactividad
Tabla comparativa de APIs de reactividad
Section titled “Tabla comparativa de APIs de reactividad”| API | Tipo de valor | Requiere .value | Reactividad anidada | Uso principal |
|---|---|---|---|---|
ref() | Cualquiera | Sí | Sí | Valores primitivos y objetos que necesitan ser reemplazados |
reactive() | Solo objetos | No | Sí | Objetos complejos con propiedades anidadas |
readonly() | Refs u objetos | Depende del origen | Sí | Prevenir modificaciones |
shallowRef() | Cualquiera | Sí | No | Mejora de rendimiento, objetos grandes |
shallowReactive() | Solo objetos | No | No | Mejora de rendimiento, objetos grandes |
toRef() | Propiedad de objeto | Sí | Sí | Mantener reactividad de una propiedad específica |
toRefs() | Objeto | Sí | Sí | Desestructurar manteniendo reactividad |