Propiedades Computadas
Ejemplo Básico
Las expresiones en las plantillas son muy convenientes, pero están pensadas para operaciones simples. Poner demasiada lógica en tus plantillas puede hacerlas infladas y difíciles de mantener. Por ejemplo, si tenemos un objeto con un array anidado:
js
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Guía Avanzada',
'Vue 3 - Guía Básica',
'Vue 4 - El Misterio'
]
})
Y queremos mostrar diferentes mensajes dependiendo de si author
ya tiene algunos libros o no:
template
<p>Ha publicado libros:</p>
<span>{{ author.books.length > 0 ? 'Sí' : 'No' }}</span>
Llegados a este punto, la plantilla se está volviendo un poco desordenada. Tenemos que mirarla un segundo antes de darnos cuenta de que realiza un cálculo en función de author.books
. Y lo que es más importante, probablemente no queramos repetirnos si necesitamos incluir este cálculo en la plantilla más de una vez.
Por eso, para una lógica compleja que incluya datos reactivos, se recomienda utilizar una propiedad computada. Aquí está el mismo ejemplo, refactorizado:
vue
<script setup>
import { reactive, computed } from 'vue'
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Guía Avanzada',
'Vue 3 - Guía Básica',
'Vue 4 - El Misterio'
]
})
// una ref computada
const publishedBooksMessage = computed(() => {
return author.books.length > 0 ? 'Sí' : 'No'
})
</script>
<template>
<p>Ha publicado libros:</p>
<span>{{ publishedBooksMessage }}</span>
</template>
Pruébalo en la Zona de Práctica
Aquí hemos declarado la propiedad computada publishedBooksMessage
. La función computed()
espera que se le pase una función getter, y el valor devuelto es una ref computada. Al igual que con las refs normales, se puede acceder al resultado calculado como publishedBooksMessage.value
. Las refs computadas también se desenvuelven automáticamente en las plantillas, por lo que se puede hacer referencia a ellas sin .value
en las expresiones de las plantillas.
Una propiedad computada rastrea automáticamente sus dependencias reactivas. Vue es consciente de que el cálculo de publishedBooksMessage
depende de author.books
, por lo que actualizará cualquier enlace que dependa de publishedBooksMessage
cuando cambie author.books
.
Véase también Escritura de computed()
Almacenamiento en Caché Computado vs. Métodos
Te habrás dado cuenta de que podemos conseguir el mismo resultado invocando un método en la expresión:
template
<p>{{ calculateBooksMessage() }}</p>
js
// en un componente
function calculateBooksMessage() {
return author.books.length > 0 ? 'Sí' : 'No'
}
En lugar de una propiedad computada, podemos definir la misma función como un método. Para el resultado final, los dos enfoques son de hecho exactamente lo mismo. Sin embargo, la diferencia es que las propiedades computadas se almacenan en caché en función de sus dependencias reactivas. Una propiedad computada sólo se reevaluará cuando alguna de sus dependencias reactivas haya cambiado. Esto significa que mientras author.books
no haya cambiado, un acceso múltiple a publishedBooksMessage
devolverá inmediatamente el resultado calculado anteriormente sin tener que volver a ejecutar la función getter.
Esto también significa que la siguiente propiedad calculada nunca se actualizará, porque Date.now()
no es una dependencia reactiva:
js
const now = computed(() => Date.now())
En comparación, una invocación a un método ejecutará siempre la función cada vez que se produzca una nueva renderización.
¿Por qué necesitamos el almacenamiento en caché? Imagina que tenemos una costosa propiedad computada lista
, que requiere recorrer un enorme array y hacer muchos cálculos. Luego podemos tener otras propiedades computadas que a su vez dependen de list
. Sin la caché, estaríamos ejecutando el getter de list
muchas más veces de las necesarias. En los casos en los que no quieras almacenar en caché, en su lugar utiliza una llamada a un método.
"Escribible" Computado
Las propiedades computadas son, por defecto, de tipo getter. Si intentas asignar un nuevo valor a una propiedad computada, recibirás una advertencia en tiempo de ejecución. En los raros casos en los que necesites una propiedad computada "escribible", puedes crear una proporcionando tanto un getter como un setter:
vue
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
const fullName = computed({
// getter
get() {
return firstName.value + ' ' + lastName.value
},
// setter
set(newValue) {
// Nota: aquí utilizamos la sintaxis de asignación de desestructuración.
;[firstName.value, lastName.value] = newValue.split(' ')
}
})
</script>
Ahora cuando ejecutes fullName.value = 'John Doe'
, el setter será invocado y firstName
y lastName
serán actualizados en consecuencia.
Mejores Prácticas
Los getters deben estar libres de efectos secundarios
Es importante recordar que las funciones getter computadas sólo deben realizar cálculos puros y estar libres de efectos secundarios. Por ejemplo, ¡no mutes otros estados, no hagas peticiones asíncronas ni mutes el DOM dentro de un getter computado!. Piensa en una propiedad computada como una descripción declarativa de cómo derivar un valor basado en otros valores: su única responsabilidad debe ser calcular y devolver ese valor. Más adelante en la guía discutiremos cómo podemos realizar efectos secundarios en reacción a los cambios de estado con los watchers.
Evitar la mutación del valor computado
El valor devuelto de una propiedad computada es un estado derivado. Piensa en ello como una instantánea temporal: cada vez que el estado fuente cambia, se crea una nueva instantánea. No tiene sentido mutar una instantánea, por lo que un valor de retorno computado debe ser tratado como de sólo lectura y nunca ser mutado; en su lugar, actualizar el estado fuente del que depende para desencadenar nuevos cálculos.