El code splitting divide tu app en archivos JavaScript más pequeños (chunks) que se cargan bajo demanda en lugar de todos a la vez. El lazy loading significa cargar un chunk solo cuando el usuario realmente lo necesita: al navegar a una ruta, abrir un modal o hacer scroll a una sección. Vite gestiona esto automáticamente cuando usas import() dinámico.
Code splitting a nivel de ruta
La división más impactante. Cada ruta se convierte en su propio chunk, cargado solo cuando el usuario navega a ella:
const routes = [
{
path: '/',
component: () => import('./views/Home.vue')
},
{
path: '/dashboard',
component: () => import('./views/Dashboard.vue')
},
{
path: '/settings',
component: () => import('./views/Settings.vue')
}
]Cada () => import(...) le dice a Vite que cree un chunk separado. El navegador descarga el código de /dashboard solo cuando el usuario navega allí. Este es el patrón por defecto en Vue Router y no requiere configuración adicional.
Code splitting a nivel de componente
Para componentes pesados dentro de una página que no siempre se necesitan:
<script setup>
import { defineAsyncComponent } from 'vue'
const HeavyChart = defineAsyncComponent({
loader: () => import('./components/HeavyChart.vue'),
loadingComponent: ChartSkeleton,
errorComponent: ChartError,
delay: 200,
timeout: 10000
})
</script>
<template>
<HeavyChart v-if="showChart" />
</template>delay evita mostrar el componente de carga en cargas rápidas (evita un flash). timeout muestra el componente de error si la carga tarda demasiado.
Para casos simples sin estados de carga/error:
const HeavyChart = defineAsyncComponent(
() => import('./components/HeavyChart.vue')
)Lazy loading condicional
Cargar componentes solo cuando se cumple una condición:
<script setup>
import { defineAsyncComponent, shallowRef } from 'vue'
const AdminPanel = shallowRef(null)
async function loadAdmin() {
AdminPanel.value = defineAsyncComponent(
() => import('./components/AdminPanel.vue')
)
}
</script>
<template>
<button @click="loadAdmin">Abrir Admin</button>
<component :is="AdminPanel" v-if="AdminPanel" />
</template>Prefetching y preloading
Vite añade automáticamente <link rel="modulepreload"> para los chunks enlazados desde el punto de entrada. Para rutas que el usuario probablemente visitará a continuación, el <RouterLink> de Vue Router no hace prefetch por defecto, pero puedes activarlo manualmente:
function prefetchRoute(path: string) {
const route = router.resolve(path)
const components = route.matched.flatMap((r) =>
Object.values(r.components ?? {})
)
components.forEach((c) => {
if (typeof c === 'function') (c as Function)()
})
}En Nuxt, <NuxtLink> hace prefetch de las rutas enlazadas automáticamente cuando entran en el viewport.
Chunks con nombre
En Webpack, los comentarios mágicos agrupan rutas relacionadas en el mismo chunk:
const routes = [
{
path: '/settings/profile',
component: () =>
import(/* webpackChunkName: "settings" */ './views/SettingsProfile.vue')
},
{
path: '/settings/billing',
component: () =>
import(/* webpackChunkName: "settings" */ './views/SettingsBilling.vue')
}
]Vite usa Rollup, que no soporta estos comentarios mágicos. Usa manualChunks en la configuración de Vite en su lugar:
// vite.config.ts
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
settings: [
'./src/views/SettingsProfile.vue',
'./src/views/SettingsBilling.vue'
]
}
}
}
}
})Qué hace Vite automáticamente
| Característica | ¿Automático? |
|---|---|
Dividir en import() dinámicos | Sí |
| Tree-shake exports no usados | Sí |
| Code splitting CSS (por componente) | Sí |
modulepreload para chunks de entrada | Sí |
| Separación de chunk de vendors | Sí (configurable) |
Cuándo dividir
| Escenario | Enfoque |
|---|---|
| Diferentes páginas/rutas | División a nivel de ruta (hazlo siempre) |
| Componente pesado detrás de un toggle | defineAsyncComponent |
| Librería grande usada en una sola página | import() dinámico en el componente |
| Sección de admin que la mayoría nunca visita | Chunk de ruta separado |
| Componentes siempre visibles en la carga | No dividir (añade latencia) |
La mayor ganancia es la división a nivel de ruta. Es el patrón por defecto en Vue Router y no cuesta nada implementarlo. La división a nivel de componente es para componentes pesados específicos donde la petición de red adicional compensa un bundle inicial más pequeño.
Ver también: ¿Qué son los componentes asíncronos? · ¿Cómo funciona Vue Router? · ¿Qué es Vite?
Referencias
- Async Components - Vue.js docs
- Lazy Loading Routes - Vue Router docs
- Code Splitting - Vite docs