问题描述
在使用vue-router
的同时使用keep-alive
是一个很常见需求。用户可能会希望某些页面进行keep-alive缓存,以方便在路由之间跳转时保留之前操作的信息。
但是在keep-alive
的同时某些情况下我们又不希望它进行缓存,我们希望某些页面是缓存的,而某些页面不进行缓存。这方面的解决方案在网上已经有很多讨论了。
项目搭建
我们首先搭建一个简易项目。我这里使用的是vite+vue3
进行项目搭建。
项目创建完成之后进行路由配置,以下是路由配置文件
:
ts
import { createRouter, createWebHistory} from "vue-router";
export const routes = [
{
path: '/',
name: "home",
component: () => import("@/pages/home.vue"),
meta: {
keepAlive: true,
title: '首页',
}
},
{
path: '/about',
name: "about",
component: () => import("@/pages/about.vue"),
meta: {
keepAlive: false,
title: "关于"
}
},
{
path: "/config",
name: "config",
component: () => import("@/pages/config.vue"),
meta: {
keepAlive: true,
title: "配置"
},
}
]
export const router = createRouter({
history: createWebHistory(),
routes,
})
然后每个页面都使用这样的简单代码:
html
<template>
<div>
Home
<input />
</div>
</template>
增加一个input
用来查看keep-alive
效果。
从配置文件可以看出来,我们设置了home 和config 两个页面是keep-alive
的,而中间的about 页面不设置keep-alive
。
这是App.vue文件初始的状态:
html
<template>
<div>
<router-link v-for="route in routes" :key="route.name" :to="route.path" style="margin-left: 16px">{{ route.meta.title }}</router-link>
</div>
<keep-alive>
<router-view></router-view>
</keep-alive>
</template>
这是浏览器的显示效果:
方案讨论
常见的方案有以下两种:
- 使用两个
router-view
分别显示不同的路由
html
<keep-alive>
<router-view v-if="$route.meta.keepAlive">
<!-- 这里是会被缓存的视图组件,比如 Home! -->
</router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive">
<!-- 这里是不被缓存的视图组件,比如 Edit! -->
</router-view>
经过我的测试发现,这种方法根本没有进行缓存,即使进行了缓存,从逻辑上来说,从不缓存页面 切换到已缓存页面 ,由于v-if
的存在,原先的缓存页面也被清除了,所以根本达不到缓存的结果。
- 使用
keep-alive
组件的include
参数配置
html
<template>
<router-view v-slot="{ Component }">
<keep-alive :include="store.cachedRoutes">
<component :is="Component" />
</keep-alive>
</router-view>
</template>
通过遍历routes
数组,将组件存到vuex
等地方,并通过keep-alive
的include
参数配置进行keep-alive
的组件。这种方案不是说不可以,只是有些繁琐,配置起来需要花费一些时间。
我的方案
我有一种非常简单(且优雅)的方案:不需要配置,不需要双router-view
,只需要借助一个小小的key
参数。
使用vue的人都知道,在渲染列表的时候,通常需要指定key
参数,有时候不指定key
参数的代码,lint
插件还会报错,浏览器控制台也会显示警告。
大家都只知道key
参数可以提高渲染速度,在vue
进行diff算法
时提供key
可以加快diff
运算。
但是key
参数还有一种神奇用法:强制重新渲染组件(或者说强制重新构建更为贴切)。正是因为diff算法
中key
的比较属于第一优先级的位置,只要组件的key
变化了,vue
会认为这个组件已经变化,这时将不再判断组件的其他成分,直接构建一个新的组件覆盖原组件。
所以解决keep-alive
情况下缓存与不缓存组件共存只需要一行简单的key
配置:
html
<router-view v-slot="{Component, route}">
<keep-alive>
<component :is="Component" :key="route.path + (route.meta.keepAlive ? '' : Math.random())" />
</keep-alive>
</router-view>
首先通过router-view
的v-slot
参数拿到route
数据。然后在component
组件上配置key
, keep-alive
的页面就使用route-path
作为key
,而不进行keep-alive
的页面就在route-path
的基础上加一个随机数。这样就可以保证非keep-alive
的页面会强制刷新。这样就解决了困扰我们已久的问题,而且还不需要额外的配置。
我称之为:一键(key
)解决方案。