Vue 3 KeepAlive: caché de rutas, preservación de estado y control de memoria

前端工程(Actualizado el 15 jul 2026)

<KeepAlive> es el componente integrado de Vue para cachear instancias inactivas. Sirve cuando el usuario sale de una página y vuelve esperando conservar filtros, formularios, scroll y estado local de UI.

También es fácil usarlo mal. Si cacheas todas las rutas, crece la memoria. Si nunca refrescas datos, los usuarios ven información antigua. Si nombres de componentes y keys no están controlados, el caché puede comportarse distinto a lo esperado.

Referencias:

Cuándo vale la pena usar KeepAlive

Úsalo en páginas donde preservar estado local mejora el flujo:

  • Listas con filtros, búsqueda, tabs y scroll.
  • Formularios multi-paso donde el usuario puede visitar otra página y volver.
  • Dashboards con renderizado local costoso.
  • Tabs de administración que no deben reiniciarse al cambiar.

Evítalo cuando:

  • La página es barata de recrear.
  • Debe mostrar datos frescos siempre.
  • Contiene datos sensibles que deben desaparecer al salir.
  • La componente tiene timers, suscripciones u objetos grandes difíciles de pausar.

El default debe ser caché selectivo, no cachear todas las rutas.

Configuración básica por ruta

Vue Router expone el componente renderizado mediante el slot de RouterView. Es el lugar limpio para envolver páginas seleccionadas con KeepAlive.

<!-- App.vue -->
<template>
  <RouterView v-slot="{ Component, route }">
    <KeepAlive :include="cachedComponentNames" :max="8">
      <component
        v-if="route.meta.keepAlive"
        :is="Component"
        :key="route.meta.cacheKey || route.name"
      />
    </KeepAlive>

    <component
      v-if="!route.meta.keepAlive"
      :is="Component"
      :key="route.fullPath"
    />
  </RouterView>
</template>

<script setup lang="ts">
const cachedComponentNames = ["UserList", "OrderList", "ReportDashboard"]
</script>

Los metadatos de ruta mantienen la decisión cerca de la ruta.

// router.ts
import type { RouteRecordRaw } from "vue-router"

export const routes: RouteRecordRaw[] = [
  {
    path: "/users",
    name: "Users",
    component: () => import("./views/UserList.vue"),
    meta: {
      keepAlive: true,
      cacheKey: "users-list"
    }
  },
  {
    path: "/users/:id",
    name: "UserDetail",
    component: () => import("./views/UserDetail.vue"),
    meta: {
      keepAlive: false
    }
  }
]

Mantén nombres de componentes estables si usas include o exclude, porque Vue hace match por nombre.

<!-- UserList.vue -->
<script setup lang="ts">
defineOptions({
  name: "UserList"
})
</script>

Refrescar datos sin reiniciar UI

El bug más común son datos viejos. Una página cacheada puede conservar resultados antiguos después de volver.

Usa onActivated para decidir si se debe refrescar. No recargues todo cada vez o perderás parte del beneficio.

<script setup lang="ts">
import { onActivated, onDeactivated, ref } from "vue"

const users = ref<User[]>([])
const loading = ref(false)
const lastLoadedAt = ref(0)
const STALE_AFTER_MS = 60_000

async function loadUsers() {
  loading.value = true
  try {
    users.value = await fetchUsers()
    lastLoadedAt.value = Date.now()
  } finally {
    loading.value = false
  }
}

onActivated(() => {
  const isStale = Date.now() - lastLoadedAt.value > STALE_AFTER_MS
  if (!users.value.length || isStale) {
    void loadUsers()
  }
})

onDeactivated(() => {
  // Pause work that should not continue while the page is hidden.
})
</script>

Así conservas estado local y aun así actualizas datos cuando envejecen.

Preservar scroll

KeepAlive conserva la instancia, pero layout, transiciones o contenedores pueden afectar el scroll.

Para un contenedor con scroll:

<template>
  <main ref="scrollEl" class="page-scroll">
    <!-- list content -->
  </main>
</template>

<script setup lang="ts">
import { nextTick, onActivated, onDeactivated, ref } from "vue"

const scrollEl = ref<HTMLElement | null>(null)
let savedTop = 0

onDeactivated(() => {
  savedTop = scrollEl.value?.scrollTop ?? 0
})

onActivated(async () => {
  await nextTick()
  if (scrollEl.value) {
    scrollEl.value.scrollTop = savedTop
  }
})
</script>

Prueba navegación directa, botón atrás y cambios programáticos de ruta.

Controlar tamaño de caché

max limita cuántas instancias conserva KeepAlive. Al superar el límite, Vue elimina entradas antiguas.

<KeepAlive :max="8">
  <component :is="Component" />
</KeepAlive>

Elige el límite según flujos reales. No lo subas demasiado para ocultar problemas de estado.

Evitar fugas de memoria

Las componentes cacheadas se desactivan, no se desmontan. Parte de la limpieza va en onDeactivated, y la limpieza final en onUnmounted.

Pausa o elimina:

  • Intervals y timeouts.
  • Listeners WebSocket o SSE que no deben correr en background.
  • ResizeObserver e IntersectionObserver.
  • Listeners globales.
  • Arrays, maps o blobs temporales grandes.
import { onActivated, onDeactivated, onUnmounted } from "vue"

let timer: number | undefined

function startPolling() {
  timer = window.setInterval(refreshStatus, 15_000)
}

function stopPolling() {
  if (timer) {
    window.clearInterval(timer)
    timer = undefined
  }
}

onActivated(startPolling)
onDeactivated(stopPolling)
onUnmounted(stopPolling)

Si la memoria crece tras navegar varias veces, perfila con devtools en vez de adivinar.

Cache keys y rutas dinámicas

Las rutas dinámicas necesitan keys cuidadosas. Si todas las páginas de detalle usan el mismo nombre y key, pueden compartir una instancia sin querer.

<!-- Share one cached instance for the user list -->
<component :is="Component" :key="route.name" />

<!-- Separate cache entry for each route parameter -->
<component :is="Component" :key="route.fullPath" />

<!-- Controlled custom key -->
<component :is="Component" :key="route.meta.cacheKey || route.fullPath" />

No uses fullPath sin pensar en listas con muchas combinaciones de query. Puede crear demasiadas entradas.

Reglas prácticas

Usa KeepAlive cuando la página tiene estado local valioso. No lo uses como interruptor genérico de rendimiento.

Buenos candidatos:

  • Listas de búsqueda.
  • Borradores de formularios.
  • Dashboards con setup costoso.
  • Tabs donde se espera cambio instantáneo.

Malos candidatos:

  • Login y autenticación.
  • Pagos o páginas sensibles.
  • Páginas que deben recargar siempre.
  • Detalles con muchos IDs sin límite claro.

Checklist final

Antes de publicar:

  • Solo rutas seleccionadas usan KeepAlive.
  • Componentes cacheados tienen nombres estables.
  • Rutas dinámicas tienen keys intencionales.
  • max es razonable.
  • Datos se refrescan con onActivated cuando hace falta.
  • Timers, observers y listeners pausan al desactivar.
  • Páginas sensibles no se cachean.
  • Botón atrás, navegación directa y tabs están probados.

KeepAlive es confiable cuando se trata como preservación de estado, no como magia automática de rendimiento.

Prueba estas herramientas que se ejecutan en tu navegador — no requieren registro →

#Vue3 KeepAlive#组件缓存#路由缓存#性能优化#include exclude#responsive component caching#layout template cache#2026#前端工程