Datos reactivos
La reactividad es el concepto más central de Vue3.
En este capítulo entenderás qué es la reactividad y aprenderás a usarrefyreactivepara gestionar datos e implementar el filtrado por categorías.
¿Qué es la reactividad?
La reactividad significa:Cuando los datos cambian, la página se actualiza automáticamente。
La forma tradicional en JS es esta: modificas la variable y aún tienes que actualizar el DOM manualmente.
El sistema reactivo de Vue3 rastrea automáticamente los cambios de datos y notifica a la vista para que se actualice — solo necesitas preocuparte por los datos, no por el DOM.
Ejemplo
import { ref } from 'vue'
// La variable envuelta por ref es reactiva
const count = ref(0)
// Después de modificar count, {{ count }} en la plantilla se actualizará automáticamente
function increment() {
count.value++ // Nota: en JS es necesario acceder con .value
}
</script>
<template>
<p>Número de clics: {{ count }}</p>
<!-- En la plantilla se desempaqueta automáticamente, no se necesita .value -->
<button @click="increment">+1</button>
</template>
Después de hacer clic en el botón, el número que se muestra en la página cambia automáticamente — eso es la reactividad.
No necesitas escribir ningúndocument.querySelectoroinnerHTMLpara actualizar la página.
ref — para tipos primitivos
refEs la API reactiva más utilizada en Vue3, adecuada para envolver datos de tipos primitivos.
Los tipos primitivos incluyen: cadenas, números, booleanos, null, undefined.
Sintaxis
Ejemplo
import { ref } from 'vue'
// Crear una ref
const name = ref('EJEMPLO') // cadena de texto
const age = ref(18) // número
const isVip = ref(false) // booleano
// Leer .value
console.log(name.value) // "EJEMPLO"
console.log(age.value) // 18
// Modificar .value
name.value = 'Hello EJEMPLO'
age.value++
isVip.value = !isVip.value
</script>
<template>
<p>Nombre: {{ name }}</p>
<!-- En la plantilla, la ref se desempaqueta automáticamente, no se necesita .value -->
<p>Edad: {{ age }}</p>
<p>VIP:{{ isVip }}</p>
</template>
El punto clave a recordar:En el código JS hay que añadir .value; en la plantilla no hace falta añadirlo. Es el error más común entre los principiantes.
reactive — para objetos y arrays
reactiveEs adecuado para envolver datos de tipos de referencia como objetos o arrays.
A diferencia de ref, reactive no necesita.value, basta con acceder directamente a las propiedades.
Ejemplo
import { reactive } from 'vue'
// Envolver un objeto con reactive
const user = reactive({
name: 'Xiaoming',
age: 20,
hobbies: ['programación', 'correr']
})
// Acceder y modificar directamente, no se necesita .value
console.log(user.name) // "Xiaoming"
user.name = 'Xiaohua' // actualización reactiva
user.hobbies.push('leer') // las operaciones con arrays también son reactivas
</script>
<template>
<p>Nombre: {{ user.name }}</p>
<p>Edad: {{ user.age }}</p>
<ul>
<li v-for="(hobby, index) in user.hobbies" :key="index">
{{ hobby }}
</li>
</ul>
</template>
Dos limitaciones de reactive
| Limitación | Descripción | Solución |
|---|---|---|
| Solo puede envolver tipos de objeto | No se pueden pasar tipos primitivos como cadenas, números, etc. | Para tipos primitivos usa ref |
| No se puede reemplazar por completo | No se puede asignar el objeto completo a un nuevo objeto | Usa Object.assign() o cambia a ref |
Ejemplo
// Error: no se puede reemplazar por completo un objeto reactive
const state = reactive({ count: 0 })
// state = { count: 1 } // Así no funciona, se pierde la reactividad
// Forma correcta 1: modifica propiedades en lugar de reemplazar todo
state.count = 1
// Forma correcta 2: envuelve el objeto con ref (ref permite reemplazar todo)
const stateRef = ref({ count: 0 })
stateRef.value = { count: 1 } // ¡Se puede! Porque el .value de ref se puede reemplazar
ref vs reactive: cuándo usar cada uno
| Escenario | Recomendación | Ejemplo |
|---|---|---|
| Una sola cadena/número/booleano | ref | const name = ref('Xiaoming') |
| Un solo array | ref | const list = ref([]) |
| Un solo objeto | ref o reactive, cualquiera sirve | const user = ref({}) |
| Varios estados no relacionados | Usa ref para cada uno | Una ref por línea |
| Un conjunto de estados fuertemente relacionados | reactive | Por ejemplo: datos de formularios, conjunto de opciones de configuración |
La práctica más común en el desarrollo real es:Usar ref en todos los casos, incluyendo objetos y arrays。
Así la escritura es uniforme y no necesitas cambiar constantemente de modelo mental entre ref y reactive.
Las secciones siguientes de este tutorial usarán ref de manera uniforme para gestionar todos los estados; cuando se necesite
.valuese indicará especialmente.
computed — propiedades computadas
computedSon "datos calculados a partir de datos existentes".
Tiene dos características principales:Almacenamiento en caché— cuando las dependencias no cambian, no se vuelve a calcular;Rastreo automático— cuando las dependencias cambian, se actualiza automáticamente.
Ejemplo
import { ref, computed } from 'vue'
const articles = ref([
{ id: 1, title: 'Introducción a Vue3', category: 'Vue' },
{ id: 2, title: 'Promise en detalle', category: 'JavaScript' },
{ id: 3, title: 'Diseño Grid', category: 'CSS' },
{ id: 4, title: 'Composition API', category: 'Vue' }
])
const activeCategory = ref('Todos')
// computed filtra los artículos según activeCategory
const filteredArticles = computed(() => {
if (activeCategory.value === 'Todos') {
return articles.value
}
return articles.value.filter(a => a.category === activeCategory.value)
})
// computed también se puede usar para estadísticas
const totalCount = computed(() => articles.value.length)
const filteredCount = computed(() => filteredArticles.value.length)
</script>
<template>
<p>Hay {{ totalCount }} artículos en total, actualmente se muestran {{ filteredCount }}</p>
<!-- Botones de categoría -->
<button @click="activeCategory = 'Todos'">Todos</button>
<button @click="activeCategory = 'Vue'">Vue</button>
<button @click="activeCategory = 'JavaScript'">JavaScript</button>
<button @click="activeCategory = 'CSS'">CSS</button>
<!-- Lista de artículos filtrados -->
<div v-for="article in filteredArticles" :key="article.id">
<h3>{{ article.title }}</h3>
<span>{{ article.category }}</span>
</div>
</template>
Cada vez queactiveCategorycambia,filteredArticlesse recalcula automáticamente y la página se actualiza sola.
Nota: computed devuelve una ref, por lo que en JS también se necesita
.value. Pero en el ejemplo anterior lo usamos directamente en la plantilla, donde se desempaqueta automáticamente.
Manos a la obra: añade el filtrado por categorías al blog
Aplica computed a la página de inicio del blog para filtrar los artículos al hacer clic en los botones de categoría.
Ejemplo
import { ref, computed } from 'vue'
const articles = ref([
{ id: 1, title: 'Guía completa de introducción a Vue3', summary: 'Aprende Vue3 desde cero', category: 'Vue', date: '2024-05-10' },
{ id: 2, title: 'Programación asíncrona en JS en detalle', summary: 'Entiende Promise y async/await', category: 'JavaScript', date: '2024-05-08' },
{ id: 3, title: 'Práctica de diseño CSS Grid', summary: 'Implementa un diseño responsivo con Grid', category: 'CSS', date: '2024-05-05' },
{ id: 4, title: 'Principio de reactividad de Vue3', summary: 'Profundiza en ref y reactive', category: 'Vue', date: '2024-05-03' },
{ id: 5, title: 'Guía completa de Flexbox', summary: 'Aprende el diseño flexible de una vez', category: 'CSS', date: '2024-05-01' },
])
// Todas las categorías (sin duplicados)
const categories = computed(() => {
const cats = articles.value.map(a => a.category)
return ['Todas', ...new Set(cats)] // Añade "Todas" al principio
})
// Categoría actualmente seleccionada
const activeCategory = ref('Todas')
// Filtrar artículos por categoría
const filteredArticles = computed(() => {
if (activeCategory.value === 'Todas') {
return articles.value
}
return articles.value.filter(a => a.category === activeCategory.value)
})
</script>
<template>
<div class="home">
<h2>Artículos recientes</h2>
<!-- Botones de filtro de categoría -->
<div class="category-bar">
<button
v-for="cat in categories"
:key="cat"
:class="{ active: activeCategory === cat }"
@click="activeCategory = cat"
>
{{ cat }}
</button>
</div>
<!-- Número de resultados filtrados -->
<p class="result-info">Total: {{ filteredArticles.length }} artículos</p>
<!-- Lista de artículos -->
<div class="article-grid">
<div v-for="article in filteredArticles" :key="article.id" class="card">
<span class="tag">{{ article.category }}</span>
<h3>{{ article.title }}</h3>
<p>{{ article.summary }}</p>
<span class="date">{{ article.date }}</span>
</div>
</div>
</div>
</template>
<style scoped>
.category-bar {
display: flex;
gap: 10px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.category-bar button {
padding: 6px 16px;
border: 1px solid #ddd;
border-radius: 20px;
background: #fff;
cursor: pointer;
font-size: 14px;
transition: all 0.2s;
}
.category-bar button.active {
background: #427DB8;
color: #fff;
border-color: #427DB8;
}
.category-bar button:hover {
border-color: #427DB8;
}
.result-info {
color: #999;
font-size: 14px;
margin-bottom: 16px;
}
</style>
Ahora, al hacer clic en los diferentes botones de categoría, la lista de artículos se filtra en tiempo real. No necesitas escribir ningún código de manipulación del DOM, solo debes prestar atención a la lógica de los cambios de datos.
Resumen del capítulo
En este capítulo has dominado las tres API principales del sistema de reactividad de Vue3: ref envuelve tipos primitivos (en JS se usa .value), reactive envuelve objetos y computed deriva datos calculados.
Mediante computed + el botón de filtro de categorías, has experimentado el patrón de desarrollo de "vista impulsada por datos": al cambiar los datos, la página se actualiza automáticamente.
Extensiones adicionales