解决痛点
最近在项目中碰到一个优化场景:订单列表页订单信息较多时,每次查看详情后返回时,由于重新调用接口导致页面重新渲染,滚轮不会停留在原始位置。造成用户每次返回都要重新下拉,体验感极差。
解决方案
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();
},
完整附件:点此下载