Vue中keep-alive 场景实现:订单列表页跳转到详情页,再从详情页跳转回列表初始停留位置
解决痛点
最近在项目中碰到一个优化场景:订单列表页订单信息较多时,每次查看详情后返回时,由于重新调用接口导致页面重新渲染,滚轮不会停留在原始位置。造成用户每次返回都要重新下拉,体验感极差。
解决方案
keep-alive组件缓存
<keep-alive>
包裹的动态组件会被缓存,它是一个抽象组件,自身不会渲染一个dom对象,也不会出现在父组件链中。当组件在<keep-alive>
内被切换时,它的activated
和deactivated
这两个生命周期钩子函数将会被对应执行。
<keep-alive :include="componentsList">
<router-view></router-view>
</keep-alive>
include(包含)属性是一个被缓存组件name的数组。其中的组件都会被缓存。
exclude(排除)属性是一个不被缓存组件name的数组。其中的组件都不会被缓存。
如果需要缓存的页面太多,在数组中一个个添加组件的name,显然有点太繁琐了。可以利用路由中的meta来进行控制我们需要进行缓存的页面。
{
path: '/first',
name: 'first',
component: () => import('../views/orderInfoList.vue'),
meta: {
keepAlive: true, //设置组件缓存
}
},
{
path: '/second',
name: 'second',
component: () => import('../views/oderInfoDetial.vue')
},
<keep-alive>
组件可以将需要缓存的组件包裹起来,并在缓存区域内保留它们的状态。这样,当组件被销毁后,它们的状态不会丢失,下次使用时可以直接从缓存中获取,避免重新渲染和数据加载。
注意:被缓存的组件八个生命周期函数会失效,并被替换成activated
和deactivated
两个周期函数。
具体实现
我们将订单列表页简称为A,订单详情页简称为B,从B跳转到A时有以下要求:
- 记录执行跳转前A页面的滚动距离。
- 不重新请求数据。
注意:只有从详情列表跳回商品列表,需要keepAlive,其他页面跳到列表页A 需要重新加载。(其他页面可能会对数列表状态进行更改,需要重新请求数据接口)
所以需要动态定义$route.meta.keepAlive
的值。
<!--修改App.vue中的router-view-->
<template>
<div id="app">
<!-- 使用keep-alive是为了缓存page-one组件内部scroll的值 -->
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
</div>
</template>
配置路由守卫函数beforeRouteEnter
和beforeRouteLeave
orderInfoList.vue
<script>
export default {
name: '',
data () {
return {
scroll: 0
}
},
beforeRouteEnter (to, from, next) {
if (from.name === 'oderInfoDetial') {
next(vm => {
const pageOneContainer = vm.$refs.pageOneContainer
// 记录滚动高度
pageOneContainer.scrollTop = vm.scroll
// 不重新请求数据
vm.notFetchData()
})
} else {
next(vm => {
const pageOneContainer = vm.$refs.pageOneContainer
// 不记录滚动高度
pageOneContainer.scrollTop = 0
// 重新请求数据
vm.fetchData()
})
}
},
beforeRouteLeave (to, from, next) {
if (to.name === 'oderInfoDetial') {
from.meta.keepAlive = true;
let myOrderContainer = this.$refs.myOrderListPage;
this.scroll = myOrderContainer.scrollTop;
} else {
from.meta.keepAlive = false;
}
next()
},
methods: {
fetchData () {
console.log('need flash')
},
notFetchData () {
console.log('do not need flash')
}
}
}
</script>
oderInfoDetial.vue
// 从订单详情页返回订单页的时候,把订单页的keepAlive值设置为true
beforeRouteLeave (to, from, next) {
if (to.name == 'pageOne') {
to.meta.keepAlive = true;
}
else{
to.meta.keepAlive = false;
}
next();
},
转载自:https://juejin.cn/post/7283798844235890723