Propiedades Computadas
Ejemplo Básico
Las expresiones en template son muy convenientes, pero están pensadas para operaciones sencillas. Poner demasiada lógica en tus templates puede hacerlas pesadas 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 mensajes diferentes dependiendo de si author ya tiene algunos libros o no:
template
<p>Ha publicado libros:</p>
<span>{{ author.books.length > 0 ? 'Sí' : 'No' }}</span>En este punto, el template se está volviendo un poco recargada. Tenemos que mirarla un segundo antes de darnos cuenta de que realiza un cálculo que depende de author.books. Más importante aún, probablemente no queramos repetirnos si necesitamos incluir este cálculo en el template más de una vez.
Por eso, para lógica compleja que incluye datos reactivos, se recomienda usar una propiedad computada. Aquí tienes 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>Aquí hemos declarado una propiedad computada publishedBooksMessage. La función computed() espera que se le pase una función getter, y el valor devuelto es una ref computada. De forma similar a las refs normales, puedes acceder al resultado computado como publishedBooksMessage.value. Las refs computadas también se desempaquetan automáticamente en los templates, por lo que puedes referenciarlas sin .value en las expresiones de template.
Una propiedad computada rastrea automáticamente sus dependencias reactivas. Vue sabe que el cálculo de publishedBooksMessage depende de author.books, por lo que actualizará cualquier enlace que dependa de publishedBooksMessage cuando author.books cambie.
Ver también: Tipado de Computed
Caché Computada vs. Métodos
Quizás hayas notado que podemos lograr el mismo resultado invocando un método en la expresión:
template
<p>{{ calculateBooksMessage() }}</p>js
// en el 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, ambos enfoques son exactamente iguales. Sin embargo, la diferencia es que las propiedades computadas se almacenan en caché basándose en sus dependencias reactivas. Una propiedad computada solo se reevaluará cuando algunas de sus dependencias reactivas hayan cambiado. Esto significa que mientras author.books no haya cambiado, los accesos múltiples a publishedBooksMessage devolverán inmediatamente el resultado computado previamente sin tener que ejecutar la función getter de nuevo.
Esto también significa que la siguiente propiedad computada nunca se actualizará, porque Date.now() no es una dependencia reactiva:
js
const now = computed(() => Date.now())En comparación, la invocación de un método siempre ejecutará la función cada vez que ocurra un re-renderizado.
¿Por qué necesitamos caché? Imagina que tenemos una propiedad computada costosa list, que requiere recorrer un array enorme y realizar muchos cálculos. Luego, podríamos tener otras propiedades computadas que a su vez dependen de list. ¡Sin caché, estaríamos ejecutando el getter de list muchas más veces de lo necesario! En los casos en que no quieras caché, utiliza una llamada a un método en su lugar.
Propiedades Computadas Escribibles
Las propiedades computadas son por defecto solo de lectura (getter-only). Si intentas asignar un nuevo valor a una propiedad computada, recibirás una advertencia en tiempo de ejecución. En los casos raros 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í estamos usando 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 se actualizarán consecuentemente.
Obteniendo el Valor Anterior
- Solo soportado en 3.4+
En caso de que lo necesites, puedes obtener el valor anterior devuelto por la propiedad computada accediendo al primer argumento del getter:
vue
<script setup>
import { ref, computed } from 'vue'
const count = ref(2)
// Este cálculo devolverá el valor de count cuando sea menor o igual a 3.
// Cuando count sea >=4, devolverá el último valor que cumpliera nuestra condición
// hasta que count sea menor o igual a 3.
const alwaysSmall = computed((previous) => {
if (count.value <= 3) {
return count.value
}
return previous
})
</script>En caso de que estés usando una propiedad computada escribible:
vue
<script setup>
import { ref, computed } from 'vue'
const count = ref(2)
const alwaysSmall = computed({
get(previous) {
if (count.value <= 3) {
return count.value
}
return previous
},
set(newValue) {
count.value = newValue * 2
}
})
</script>Mejores Prácticas
Los getters deben estar libres de efectos secundarios
Es importante recordar que las funciones getter computadas solo deben realizar cálculos puros y estar libres de efectos secundarios. Por ejemplo, ¡no mutar otro estado, realizar solicitudes asíncronas o mutar el DOM dentro de un getter computado! Piensa en una propiedad computada como la 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 watchers.
Evitar mutar valores computados
El valor devuelto por una propiedad computada es un estado derivado. Piénsalo como una instantánea temporal: cada vez que el estado de origen cambia, se crea una nueva instantánea. No tiene sentido mutar una instantánea, por lo que un valor de retorno computado debe tratarse como de solo lectura y nunca ser mutado; en su lugar, actualiza el estado de origen del que depende para activar nuevos cálculos.