Vue 3 KeepAlive: кеш маршрутов, сохранение состояния и контроль памяти

前端工程(Обновлено 15 июл. 2026 г.)

<KeepAlive> — встроенный компонент Vue для кеширования неактивных экземпляров компонентов. Он полезен, когда пользователь уходит со страницы и возвращается, ожидая сохранить фильтры, форму, scroll position и локальное UI-состояние.

KeepAlive легко использовать неправильно. Если кешировать все маршруты, растет память. Если данные никогда не обновляются, пользователь видит старую информацию. Если имена компонентов и route keys не контролируются, кеш может вести себя неожиданно.

Ссылки:

Когда KeepAlive полезен

Используйте KeepAlive для страниц, где сохранение локального состояния улучшает workflow:

  • Списки с фильтрами, поиском, вкладками и scroll position.
  • Многошаговые формы, где пользователь может уйти и вернуться.
  • Dashboards с дорогим локальным rendering.
  • Admin tabs, которые не должны сбрасываться.

Избегайте KeepAlive, если:

  • Страницу дешево создать заново.
  • Страница всегда должна показывать свежие данные.
  • На странице есть sensitive data, которые должны исчезать при уходе.
  • Компонент держит timers, subscriptions или большие objects, которые сложно поставить на паузу.

По умолчанию кеш должен быть выборочным, а не для всех маршрутов.

Базовая настройка route-level cache

Vue Router передает rendered route component через slot RouterView. Это удобное место для 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>

Route metadata держат решение рядом с определением route.

// 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
    }
  }
]

Держите имена компонентов стабильными при использовании include или exclude, потому что Vue сопоставляет кеш по имени.

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

Обновлять данные без сброса UI

Самая частая ошибка — устаревшие данные. Кешированная страница может хранить старый API result после возврата.

Используйте onActivated, чтобы решить, нужно ли обновление. Не перезагружайте все при каждой активации.

<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>

Так сохраняется локальный UI-state, но старые данные обновляются.

Сохранение scroll position

KeepAlive сохраняет экземпляр компонента, но scroll может зависеть от layout, transitions или контейнера.

<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>

Проверьте direct navigation, back button и programmatic route changes.

Ограничить размер кеша

max ограничивает число instances, которые KeepAlive хранит. При превышении Vue удаляет старые cache entries.

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

Выбирайте лимит по реальным workflows. Не ставьте большое число, чтобы скрыть проблемы управления состоянием.

Избегать утечек памяти

Кешированные компоненты deactivated, но не unmounted. Поэтому часть cleanup должна быть в onDeactivated, финальная — в onUnmounted.

Пауза или удаление:

  • Intervals и timeouts.
  • WebSocket или SSE listeners, которые не должны работать в background.
  • ResizeObserver и IntersectionObserver.
  • Global event listeners.
  • Большие временные arrays, maps или blobs.
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)

Если память растет после повторной навигации, используйте browser devtools.

Cache keys и динамические маршруты

Динамическим маршрутам нужны аккуратные keys. Если detail pages используют одно имя и один key, они могут разделить cache instance неосознанно.

<!-- 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" />

Не используйте fullPath бездумно для списков с множеством query combinations. Это может создать слишком много entries.

Практические правила

Используйте KeepAlive, когда у страницы есть ценный local state. Не используйте как общий performance switch.

Хорошие кандидаты:

  • Search result lists.
  • Form drafts.
  • Dashboards с дорогим chart setup.
  • Tabs, где ожидается instant switching.

Плохие кандидаты:

  • Authentication screens.
  • Payment или security-sensitive pages.
  • Страницы, которые должны refetch каждый раз.
  • Detail pages с большим числом unique IDs без clear cache limit.

Финальный checklist

Перед релизом проверьте:

  • Только выбранные routes используют KeepAlive.
  • Cached components имеют стабильные names.
  • Dynamic routes имеют intentional keys.
  • max установлен разумно.
  • Data refresh использует onActivated, когда нужно.
  • Timers, observers и listeners паузятся при deactivation.
  • Sensitive pages не кешируются.
  • Back button, direct navigation и tab switching протестированы.

KeepAlive надежен, когда используется для сохранения состояния, а не как автоматическая performance magic.

Попробуйте эти локальные браузерные инструменты — регистрация не требуется →

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