Navegación de rutas
En este capítulo aprenderás a usar Vue Router para implementar navegación entre páginas con separación de frontend y backend, incluidos los parámetros de ruta dinámicos.
¿Por qué usar enrutamiento?
En los sitios web multipágina tradicionales, al hacer clic en un enlace se solicita al servidor un nuevo archivo HTML, la página se recarga por completo y se produce un parpadeo en blanco.
Lo que Vue Router implementa esenrutamiento de frontend: cuando la URL cambia, no se envía una solicitud al servidor, sino que JavaScript alterna dinámicamente los componentes mostrados.
Beneficios de hacerlo así:Cambio de página sin recarga, con una experiencia tan fluida como la de una aplicación nativa。
Conceptos fundamentales de Vue Router
| Concepto | Función | Código correspondiente |
|---|---|---|
| Tabla de rutas | Define la relación de mapeo entre la ruta URL y el componente | routesArray |
| Salida de enrutamiento | Lugar donde se muestra el componente coincidente | <RouterView /> |
| Enlaces de navegación | Al hacer clic navega a la ruta especificada | <RouterLink to="..."> |
| Ruta dinámica | La URL contiene variables (por ejemplo, el ID del artículo) | /post/:id |
Instalación y configuración
Si seleccionaste Router al crear el proyecto, puedes omitir los pasos de instalación.
Si no lo seleccionaste, ejecuta en la terminal:
$ npm install vue-router@4
Crear el archivo de configuración de rutas
Ejemplo
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
// Tabla de rutas: cada registro asigna una ruta a un componente
const routes = [
{
path: '/', // Ruta URL
name: 'home', // Nombre de la ruta (opcional, útil para la navegación programática)
component: HomeView // Componente de página correspondiente
},
{
// Ruta dinámica: :id es un marcador de posición, coincide con cualquier valor
// /post/1, /post/2, /post/hello coinciden con esta
path: '/post/:id',
name: 'post',
// Carga diferida: se carga solo al acceder, y durante el empaquetado se divide en archivos JS independientes
component: () => import('../views/PostView.vue')
}
]
// Crear la instancia del enrutador
const router = createRouter({
history: createWebHistory(), // Usar el modo History de HTML5 (la URL parece una ruta normal)
routes // Registrar la tabla de rutas
})
export default router
Diferencia entre los dos modos de enrutamiento:createWebHistoryProduce URLs limpias (/post/1), requiere soporte de configuración del servidor. Si el entorno de despliegue no es seguro, puedes usarcreateWebHashHistory, la URL incluye el símbolo # (/#/post/1), no requiere configuración del servidor.
Registrar el enrutador en main.js
Ejemplo
<script setup>
import NavBar from './components/NavBar.vue'
</script>
<template>
<NavBar />
<main>
<!-- RouterView es la ubicación mágica: el componente que coincide con la ruta actual se renderiza aquí -->
<RouterView />
</main>
</template>
RouterViewEs la salida principal del sistema de enrutamiento.
Cuando la URL es/, renderiza HomeView; cuando la URL es/post/1, renderiza PostView.
RouterLink — Navegación declarativa
RouterLinkes el componente de navegación proporcionado por Vue Router, que reemplaza a la<a>etiqueta <a> tradicional.
No provoca una recarga de la página; solo cambia el componente mostrado mediante JavaScript.
Ejemplo
<!-- Escritura normal -->
<RouterLink to="/">Inicio</RouterLink>
<!-- Escritura con ruta con nombre (recomendada: si cambia la ruta, solo se modifica en un lugar) -->
<RouterLink :to="{ name: 'post', params: { id: article.id } }">
Leer artículo {{ article.id }}
</RouterLink>
<!-- La etiqueta a tradicional provoca recarga de página, no la uses -->
<!-- <a href="/post/1">Leer el artículo completo</a> -->
</template>
useRoute — Lectura de parámetros de ruta
En la página de detalle PostView, necesitas saber cuál es el ID del artículo en la URL para poder encontrar el artículo correspondiente.
useRoutese usa para leer la información de la ruta actual.
Ejemplo
<script setup>
import { useRoute } from 'vue-router'
// useRoute devuelve el objeto de ruta actual
const route = useRoute()
// route.params contiene los parámetros de la URL
// Por ejemplo, si la URL es /post/3, entonces route.params.id === '3'
console.log('ID del artículo:', route.params.id)
</script>
<template>
<div>
<h1>Página de detalle del artículo</h1>
<!-- Nota: los valores en params son cadenas de texto -->
<p>ID del artículo actual: {{ route.params.id }}</p>
</div>
</template>
route.params.idSu valor siempre es una cadena de texto. Incluso si la URL es/post/3, el valor obtenido también es'3'y no un número3. Cuando necesites un número, usaNumber(route.params.id)oparseIntpara convertirlo.
Manos a la obra: página completa de detalle de artículo
Modifica BlogCard para que sea clicable y navegue, y que la página de detalle muestre el contenido completo del artículo según el ID.
Primer paso: modificar BlogCard para admitir navegación al hacer clic
Ejemplo
<script setup>
defineProps({
id: Number, // Nuevo: ID del artículo, se usa para la navegación
title: String,
summary: String,
date: String,
category: String
})
</script>
<template>
<!-- La tarjeta completa está envuelta en RouterLink, al hacer clic en cualquier parte se navega -->
<RouterLink :to="{ name: 'post', params: { id } }" class="card-link">
<div class="card">
<span class="tag">{{ category }}</span>
<h3>{{ title }}</h3>
<p>{{ summary }}</p>
<span class="date">{{ date }}</span>
</div>
</RouterLink>
</template>
<style scoped>
.card-link { text-decoration: none; color: inherit; display: block; }
.card {
background: #fff;
border-radius: 12px;
padding: 20px;
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
transition: transform 0.2s, box-shadow 0.2s;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0,0,0,0.12);
}
.tag {
display: inline-block;
padding: 2px 10px;
background: #E8EEF5;
color: #427DB8;
border-radius: 12px;
font-size: 12px;
}
.card h3 { margin: 10px 0 8px; font-size: 18px; }
.card p { color: #666; font-size: 14px; line-height: 1.6; }
.date { font-size: 12px; color: #999; }
</style>
Segundo paso: crear la página de detalle PostView
La página de detalle lee el ID de la URL, luego busca el artículo correspondiente en los datos de artículos y lo muestra.
Ejemplo
<script setup>
import { ref, computed } from 'vue'
import { useRoute, RouterLink } from 'vue-router'
const route = useRoute()
const id = Number(route.params.id)
// Todos los datos de artículos (en capítulos posteriores se extraerán a un módulo compartido)
const articles = ref([
{
id: 1,
title: 'Guía completa de introducción a Vue3',
summary: 'Aprende desde cero la API de composición de Vue3',
category: 'Vue',
date: '2024-05-10',
content: `
<h2>¿Por qué aprender Vue3?</h2>
<p>Vue3 es uno de los frameworks de frontend más populares actualmente; es fácil de aprender, flexible y tiene un rendimiento excelente.</p>
<h2>Ventajas de la Composition API</h2>
<p>En comparación con la Options API de Vue2, la Composition API hace que la reutilización de lógica sea más sencilla y que la organización del código sea más flexible.</p>
<h2>ref y reactive</h2>
<p>ref es adecuado para datos de tipos primitivos; reactive para objetos y arrays. Pero en el desarrollo real se recomienda usar ref de manera uniforme.</p>
`
},
{
id: 2,
title: 'Guía detallada de programación asíncrona en JS',
summary: 'Entiende Promise y async/await',
category: 'JavaScript',
date: '2024-05-08',
content: `
<h2>¿Qué es la asincronía?</h2>
<p>JS es de un solo subproceso; las operaciones asíncronas permiten que el subproceso principal no se bloquee y manejen solicitudes de red, temporizadores y otras tareas al mismo tiempo.</p>
<h2>Conceptos básicos de Promise</h2>
<p>Promise tiene tres estados: pending (en curso), fulfilled (cumplida), rejected (rechazada).</p>
`
}
])
// Buscar el artículo según el ID
const article = computed(() => {
return articles.value.find(a => a.id === id)
})
</script>
<template>
<div class="post-view">
<!-- El artículo no existe -->
<div v-if="!article" class="not-found">
<h2>El artículo no existe</h2>
<p>No se encuentra el artículo con ID {{ id }}</p>
<RouterLink to="/">Volver al inicio</RouterLink>
</div>
<!-- El artículo existe, mostrar el contenido completo -->
<article v-else>
<span class="category-tag">{{ article.category }}</span>
<h1>{{ article.title }}</h1>
<time>{{ article.date }}</time>
<!-- v-html renderiza cadenas HTML -->
<div class="content" v-html="article.content"></div>
<RouterLink to="/" class="back-link">← Volver al inicio</RouterLink>
</article>
</div>
</template>
<style scoped>
.post-view {
max-width: 720px;
margin: 40px auto;
padding: 0 20px;
}
.category-tag {
display: inline-block;
padding: 4px 12px;
background: #E8EEF5;
color: #427DB8;
border-radius: 12px;
font-size: 13px;
margin-bottom: 12px;
}
h1 {
font-size: 32px;
margin-bottom: 12px;
line-height: 1.4;
}
time {
display: block;
color: #999;
font-size: 14px;
margin-bottom: 30px;
}
.content {
line-height: 1.8;
font-size: 16px;
color: #333;
}
.content :deep(h2) {
margin: 24px 0 12px;
font-size: 22px;
}
.content :deep(p) {
margin-bottom: 12px;
}
.back-link {
display: inline-block;
margin-top: 40px;
color: #427DB8;
text-decoration: none;
}
.not-found {
text-align: center;
padding: 60px 0;
}
</style>
v-htmlSe usa para renderizar cadenas HTML, pero ten en cuenta: no lo utilices para renderizar contenido introducido por el usuario, de lo contrario existe riesgo de seguridad XSS. Aquí el contenido del artículo está escrito por el desarrollador, por lo que es seguro.
Resumen del capítulo
En este capítulo aprendiste el uso fundamental de Vue Router: configuración de la tabla de rutas (path asignado a component), navegación con RouterLink, salida RouterView, rutas dinámicas /post/:id y lectura de parámetros de ruta con useRoute.
Ahora el blog tiene una verdadera experiencia multipágina: en la página de inicio se navega por la lista, y al hacer clic se entra a la página de detalles para leer el contenido completo.
Otras extensiones