Lazy loading de rutas
En una aplicación Vue con muchas vistas, importar todos los componentes al inicio genera un bundle grande que ralentiza la primera carga. El lazy loading permite cargar cada vista únicamente cuando el usuario navega hacia ella, dividiendo el código en fragmentos más pequeños.
Importaciones estáticas vs dinámicas
Con importaciones estáticas, todos los componentes se incluyen en el bundle principal:
// Importación estática: se incluye siempre en el bundle
import HomeView from './views/HomeView.vue'
import AboutView from './views/AboutView.vue'
import ContactView from './views/ContactView.vue'
Con importaciones dinámicas, cada componente se carga bajo demanda usando import():
// Importación dinámica: se carga solo cuando el usuario navega a la ruta
const HomeView = () => import('./views/HomeView.vue')
const AboutView = () => import('./views/AboutView.vue')
const ContactView = () => import('./views/ContactView.vue')
Configuración de rutas con lazy loading
La forma más habitual es definir las importaciones dinámicas directamente en el array de rutas:
// router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
name: 'home',
component: () => import('../views/HomeView.vue')
},
{
path: '/about',
name: 'about',
component: () => import('../views/AboutView.vue')
},
{
path: '/dashboard',
name: 'dashboard',
component: () => import('../views/DashboardView.vue')
},
{
path: '/profile/:id',
name: 'profile',
component: () => import('../views/ProfileView.vue')
},
{
path: '/settings',
name: 'settings',
component: () => import('../views/SettingsView.vue')
}
]
})
export default router
Vite detecta automáticamente cada import() y genera un chunk separado para cada vista. Al abrir la aplicación, solo se descarga el código de la ruta inicial. Cuando el usuario navega a otra vista, el navegador descarga el chunk correspondiente en ese momento.
Beneficios del lazy loading
- Bundle inicial más pequeño: la aplicación arranca más rápido.
- Carga bajo demanda: el código de cada vista se descarga solo si el usuario la visita.
- Mejor rendimiento percibido: el tiempo hasta que la aplicación es interactiva se reduce.
Agrupación de rutas en chunks
En ocasiones conviene agrupar varias rutas relacionadas en un mismo chunk para evitar demasiadas peticiones de red. Con Vite se puede utilizar un comentario magico en el import():
const routes = [
{
path: '/admin',
component: () => import(/* webpackChunkName: "admin" */ '../views/AdminLayout.vue'),
children: [
{
path: 'users',
component: () => import(/* webpackChunkName: "admin" */ '../views/AdminUsers.vue')
},
{
path: 'stats',
component: () => import(/* webpackChunkName: "admin" */ '../views/AdminStats.vue')
}
]
}
]
En proyectos con Vite puro, la agrupación se gestiona de forma automática mediante la configuración de rollupOptions en vite.config.ts si se necesita un control más fino.
Prefetching y preloading
Los navegadores modernos permiten prefetch de recursos que probablemente se necesitarán pronto. Vite genera automáticamente directivas <link rel="modulepreload"> para los chunks que el usuario podría necesitar a continuación.
Para un prefetching más controlado, se puede importar manualmente un módulo sin renderizarlo:
// Prefetch manual al pasar el raton sobre un enlace
function prefetchAbout() {
import('../views/AboutView.vue')
}
<template>
<RouterLink to="/about" @mouseenter="prefetchAbout">Acerca de</RouterLink>
</template>
Componente de carga mientras la ruta carga
Se puede mostrar un componente de carga mientras un chunk se descarga usando defineAsyncComponent o el componente <Suspense>:
<!-- App.vue -->
<template>
<nav>
<RouterLink to="/">Inicio</RouterLink>
<RouterLink to="/about">Acerca de</RouterLink>
</nav>
<Suspense>
<template #default>
<RouterView />
</template>
<template #fallback>
<div class="loading">Cargando vista...</div>
</template>
</Suspense>
</template>
<script setup lang="ts">
</script>
Scroll behavior
Por defecto, al navegar entre rutas en una SPA, la posición de scroll se mantiene. Vue Router permite personalizar el comportamiento de scroll mediante la opción scrollBehavior en la configuración del router.
Configuración básica
La función scrollBehavior recibe tres parámetros: la ruta destino (to), la ruta origen (from) y la posición guardada (savedPosition):
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [/* ... */],
scrollBehavior(to, from, savedPosition) {
// Retorna la posición deseada
}
})
Scroll al inicio en cada navegación
El caso más común es hacer scroll al inicio de la página cada vez que el usuario navega a una nueva ruta:
scrollBehavior() {
return { top: 0 }
}
Restaurar posición guardada
Cuando el usuario usa los botones atrás/adelante del navegador, es mejor restaurar la posición en la que estaba. El parámetro savedPosition contiene esa información:
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
}
return { top: 0 }
}
Scroll a un ancla con hash
Si la URL contiene un hash (por ejemplo /about#equipo), se puede hacer scroll al elemento con ese id:
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
}
if (to.hash) {
return { el: to.hash }
}
return { top: 0 }
}
Scroll suave
Para aplicar una animación de scroll suave, se añade la propiedad behavior: 'smooth' al objeto retornado:
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return { ...savedPosition, behavior: 'smooth' }
}
if (to.hash) {
return { el: to.hash, behavior: 'smooth' }
}
return { top: 0, behavior: 'smooth' }
}
Scroll asíncrono
A veces es necesario esperar a que una transición o animación termine antes de hacer scroll. La función scrollBehavior puede devolver una promesa:
scrollBehavior(to, from, savedPosition) {
return new Promise((resolve) => {
setTimeout(() => {
if (savedPosition) {
resolve(savedPosition)
} else if (to.hash) {
resolve({ el: to.hash, behavior: 'smooth' })
} else {
resolve({ top: 0 })
}
}, 300) // espera 300ms para la transicion
})
}
Ejemplo completo
A continuación se muestra una configuración completa del router con lazy loading en todas las rutas y scroll behavior combinando todas las estrategias:
// router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import type { RouteRecordRaw } from 'vue-router'
const routes: RouteRecordRaw[] = [
{
path: '/',
name: 'home',
component: () => import('../views/HomeView.vue')
},
{
path: '/about',
name: 'about',
component: () => import('../views/AboutView.vue')
},
{
path: '/blog',
name: 'blog',
component: () => import('../views/BlogView.vue')
},
{
path: '/blog/:slug',
name: 'blog-post',
component: () => import('../views/BlogPostView.vue')
},
{
path: '/contact',
name: 'contact',
component: () => import('../views/ContactView.vue')
}
]
const router = createRouter({
history: createWebHistory(),
routes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return { ...savedPosition, behavior: 'smooth' }
}
if (to.hash) {
return { el: to.hash, behavior: 'smooth' }
}
return { top: 0, behavior: 'smooth' }
}
})
export default router
Componente de navegacion que demuestra los tres comportamientos:
<!-- NavBar.vue -->
<template>
<nav class="navbar">
<!-- Navegacion normal: scroll al inicio -->
<RouterLink to="/">Inicio</RouterLink>
<RouterLink to="/about">Acerca de</RouterLink>
<RouterLink to="/blog">Blog</RouterLink>
<RouterLink to="/contact">Contacto</RouterLink>
<!-- Navegacion con hash: scroll al ancla -->
<RouterLink to="/about#equipo">Equipo</RouterLink>
<RouterLink to="/about#historia">Historia</RouterLink>
</nav>
</template>
<script setup lang="ts">
</script>
<style scoped>
.navbar {
display: flex;
gap: 1rem;
padding: 1rem;
background: #f8f9fa;
border-bottom: 1px solid #dee2e6;
}
.navbar a {
text-decoration: none;
color: #007bff;
font-weight: 500;
}
.navbar a.router-link-active {
color: #0056b3;
border-bottom: 2px solid #007bff;
}
</style>
Con esta configuración, al hacer clic en "Acerca de" la página carga el chunk correspondiente y hace scroll suave al inicio. Al hacer clic en "Equipo", navega a /about#equipo y hace scroll suave hasta el elemento con id="equipo". Al pulsar el botón atrás del navegador, se restaura la posición de scroll anterior.
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Vuejs es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de Vuejs
Explora más contenido relacionado con Vuejs y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Cursos que incluyen esta lección
Esta lección forma parte de los siguientes cursos estructurados con rutas de aprendizaje