一、引言:为什么需要路由缓存?
在 Vue 开发中,我们经常遇到这样的场景:
- 从列表页进入详情页,返回列表页时希望保留之前的滚动位置和筛选条件;
- 频繁切换的标签页(如后台管理系统的 tabs),希望保留每个标签页的状态;
- 避免重复请求数据,提升页面切换的流畅度。
这时,路由缓存就成了关键。Vue3 中通过 <keep-alive>
组件结合路由元信息,可轻松实现路由级别的状态保留。本文将从基础使用到进阶技巧,全面讲解 Vue3 路由缓存的实现方法。
二、基础实现:快速上手路由缓存
1. 第一步:配置路由元信息
在路由配置中,通过 meta
字段标记需要缓存的路由:
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import Home from '@/views/Home.vue';
import About from '@/views/About.vue';
import UserList from '@/views/UserList.vue'; // 需缓存的列表页
const routes = [
{
path: '/',
name: 'Home',
component: Home,
meta: { keepAlive: true } // ✅ 标记为需要缓存
},
{
path: '/about',
name: 'About',
component: About,
meta: { keepAlive: false } // ❌ 不需要缓存
},
{
path: '/user-list',
name: 'UserList',
component: UserList,
meta: { keepAlive: true } // ✅ 需要缓存
}
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
});
export default router;
2. 第二步:在 App.vue 中使用 <keep-alive>
通过 Vue Router 的插槽语法,将需要缓存的组件包裹在 <keep-alive>
中:
<!-- src/App.vue -->
<template>
<div id="app">
<router-view v-slot="{ Component }">
<!-- 缓存标记为 keepAlive: true 的路由 -->
<keep-alive>
<component
:is="Component"
v-if="route.meta.keepAlive"
:key="route.fullPath" <!-- 关键:用完整路径作为键,避免同一路由不同参数的缓存冲突 -->
/>
</keep-alive>
<!-- 不缓存的路由直接渲染 -->
<component
:is="Component"
v-if="!route.meta.keepAlive"
:key="route.fullPath"
/>
</router-view>
</div>
</template>
3. 关键说明:
v-slot="{ Component }"
:Vue Router 4.x 中,router-view
支持插槽传递当前组件;keep-alive
:Vue 内置组件,用于缓存包裹的组件实例;key="route.fullPath"
:确保不同路由(即使路径相同但参数不同,如/user/1
和/user/2
)生成不同的缓存实例,避免数据混淆;meta.keepAlive
:通过路由元信息控制是否缓存。
三、进阶技巧:灵活控制缓存行为
1. 动态控制缓存:根据路由参数决定是否缓存
有时需要根据路由参数或查询参数动态决定是否缓存,比如从列表页进入详情页时缓存列表页:
// 路由配置
{
path: '/user-list',
name: 'UserList',
component: UserList,
meta: {
keepAlive: (to, from) => {
// 只有从详情页返回时才缓存列表页
return from.name === 'UserDetail';
}
}
}
在 App.vue 中需要调整判断逻辑:
<keep-alive>
<component
:is="Component"
v-if="typeof route.meta.keepAlive === 'function'
? route.meta.keepAlive(route, router.currentRoute.value)
: route.meta.keepAlive"
:key="route.fullPath"
/>
</keep-alive>
2. 条件性缓存:包含/排除特定组件
使用 keep-alive
的 include
和 exclude
属性,精确控制缓存的组件:
<template>
<router-view v-slot="{ Component }">
<keep-alive
:include="['UserList', 'ProductList']" <!-- 只缓存这两个组件 -->
:exclude="['About']" <!-- 不缓存这个组件 -->
:max="5" <!-- 最多缓存5个组件,避免内存溢出 -->
>
<component :is="Component" :key="route.fullPath" />
</keep-alive>
</router-view>
</template>
3. 保持滚动位置:提升用户体验
在路由配置中,通过 scrollBehavior
保持滚动位置:
// router/index.js
const router = createRouter({
history: createWebHistory(),
routes,
scrollBehavior(to, from, savedPosition) {
// 如果是缓存的路由,恢复之前的滚动位置
if (savedPosition && to.meta.keepAlive) {
return savedPosition;
}
// 否则回到顶部
return { top: 0 };
}
});
4. 使用生命周期钩子:缓存组件的更新逻辑
被缓存的组件不会触发 onMounted
和 onUnmounted
,但会触发激活/停用钩子:
<!-- src/views/UserList.vue -->
<template>
<div class="user-list">
<h2>用户列表</h2>
<ul>
<li v-for="user in users" :key="user.id">{{ user.name }}</li>
</ul>
</div>
</template>
<script setup>
import { ref, onActivated, onDeactivated } from 'vue';
import { fetchUsers } from '@/api/user';
const users = ref([]);
// 组件被激活时调用(如从详情页返回)
onActivated(async () => {
console.log('UserList 被激活');
// 可以在这里更新数据(如刷新列表)
users.value = await fetchUsers();
});
// 组件被停用时调用(如进入详情页)
onDeactivated(() => {
console.log('UserList 被停用');
// 可以在这里清理资源(如取消请求)
});
</script>
手动清除缓存
对于需要强制刷新的场景,可以手动清除缓存:
// 在组件中清除指定路由的缓存
import { getCurrentInstance } from 'vue';
const instance = getCurrentInstance();
// 清除 UserList 组件的缓存
instance.appContext.config.globalProperties.$cache.delete('UserList');