KeepAlive在路由中的应用——保存页面状态
最近业务开发过程中遇到一个很有意思的场景:列表页跳详情页,再折返回列表页时,需要还原离开时的状态。通常的做法是:离开时存储状态 --> 返回时还原状态。但实际上大部分情况下,我们完全可以省略这个步骤。我们只要缓存需要保存状态的页面,使其在离开时不被销毁,并在返回重新激活即可。
这其实是KeepAlive
组件的一个典型使用场景,只不过此时KeepAlive的目标变成了整个页面,而非页面的一角,但本质是一样的。
业务场景
假设我们有这样一个场景:在列表页输入查询条件远程搜索到符合条件的n条数据,然后点进某一项的详情页面,查看完信息之后,我们要返回列表页,查看其他项目的详情页。
在没有做任何处理的情况下,页面将会重新加载,之前的搜索条件将被清空,搜索到的数据也会丢失。想要回到之前的状态,用户只能重新搜索,然后继续之前的操作。这听起来就很麻烦,特别是当网络不好的时候😔。
上面简单描述了业务场景,如果你还是不太清楚我在说什么的话,可以参考下面这张GIF:
- 首先我们在
manage
页面进行筛选。 - 随后点击选项查看详情,跳转到
detail
页面。从DevTools中可以看到,ListManage组件并未卸载,而是进入了inactive
状态。 - 然后折返
manage
页面。ListManage组件的状态由inactive
→active
可以看到,此时的manage
页面与离开时一模一样,这就是我们想要实现的效果。
方案对比
常见方案主要有:跳转新页面、手动保存状态、KeepAlive,三种方案各有优劣。
- 跳转新页面:最简单的实现方式,打开一个新的浏览器Tab页即可保留原始页面。由于要新开页面,使用体验略显割裂。
- 手动保存状态:此种方式通常需要依赖全局存储,复杂程度和业务逻辑挂钩,页面越复杂,需要控制的细节越多,该方式也就越复杂。以过去的编码经验来讲,业务组件依赖全局状态,很容易滋生BUG,尤其是在多人合作的情况下,这需要做一些取舍了。
- KeepAlive: 复杂程度适中,但有一定的门槛。需要对VueRouter的路由配置方式有一定的理解,否则很可能弄巧成拙。但这种方式确是使用体验最好的一种。
KeepAlive具体实现
前两种实现方式没什么多说的,就是字面意思是,本文主要探讨KeepAlive的实现方式。
首先我们需要一个辅助组件,我们暂时命名为RouterAlive
。这是一个非常简单的基于KeepAlive和RouterView封装的路由根组件。
import { defineComponent, KeepAlive } from 'vue';
import { RouterView } from 'vue-router';
export const RouterAlive = defineComponent({
name: 'RouterAlive',
setup(props, ctx) {
return () => (
<RouterView
v-slots={{
default: ({ Component }) => (
<KeepAlive {...ctx.attrs}>
<Component />
</KeepAlive>
),
}}
/>
);
},
});
然后我们在路由配置中引用该组件,并设置我们需要缓存的组件,此处我们要缓存列表页组件ListManage
,配置如下:
export default [
{
path: '/list',
redirect: '/list/manage',
component: RouterAlive, // ① 我们封装的组件
props: { include: 'ListManage' }, // ② 设定我们想要缓存的组件
children: [
{
path: 'manage',
meta: { name: '列表页' },
component: ListManage, // ③ 需要保证该组件的名称能够被KeepAlive匹配
},
{
path: 'detail/:listId(\\d+)',
meta: { name: '详情页' },
props: route => ({ listId: route.params.listId }),
component: ItemDetail,
},
{
path: 'edit/:listId(\\d+)',
meta: { name: '编辑页' },
props: route => ({ listId: route.params.listId }),
component: EditItem,
},
]
}
]
上面的配置达成的效果是:在RouterAlive
组件下配置的children路由中
随意跳转时,设定缓存的组件将会自动缓存和激活,而不会卸载。需要注意的是,如果目标路由脱离了children路由的范畴,那缓存的组件还是会被卸载。
通常我们会把相关的一组路由放在一起,配合RouterAlive
可以极大地提升页面跳转性能和用户体验。具体怎么分组可以根据业务场景灵活配置。但如果你几乎从未使用过children路由,而是将所有路由拍平成一个一维数组,那么你可能需要重新审视一下你的路由配置方式了,可以参考另一篇文章——你可能没用过的,VueRouter高级匹配模式
经过上面的开发,现在假设我们从列表页出发,跳转到详情页也好,编辑页也好,当用户完成一堆操作并返回最初的列表页时,只要没有刷新页面,都可以无缝衔接之前的上下文,继续工作。这在一定程度上提升了用户体验和平台使用效率。
需要注意的是,KeepAlive组件依赖组件名称进行匹配,如果被缓存的组件没有提供name选项,则无法正确被缓存,常见于使用了
script setup
语法糖的vue组件,这类组件暂时无法提供name选项,因此无法被KeepAlive缓存。由于被缓存的组件将会多出onActivited/onDeactivited
两个生命周期函数,原组件生命周期可能需要根据情况调整。
总结
通过VueRouter
与RouterAlive
的巧妙结合,我们可以方便地实现页面缓存的需求,且同时兼顾用户体验和页面性能。正所谓站在巨人的肩膀上,相比通过手动编写页面状态控制逻辑而实现的“伪缓存”效果,该方案具备诸多优势,例如,代码具备通用性、且对原页面的代码注入较少等。
如有错误,欢迎斧正。
转载自:https://juejin.cn/post/7139090134695149604