vue-router后退缓存(数据和scroll的位置)

本文探讨了在Vue的SPA应用中,如何利用vue-router和keep-alive组件实现页面后退缓存,同时解决缓存滚动位置和控制页面是否重新加载数据的问题。通过设置路由元信息、监听滚动事件以及在activated钩子中判断是否刷新数据,成功实现了业务需求。然而,部分场景下仍存在代码侵入,如底部tabbar切换时的滚动状态保存。

业务场景

我们在使用APP或者小程序的时候,从商品的详情页回到列表页,都会发现列表页面是没有刷新的,还是之前的状态。但是Vue的SPA(单页面应用)在使用vue-router的情况下,每次路由都会刷新页面,这和我们的业务场景是不相符的,会给用户带来不好的操作体验。

keep-alive组件

首页,必须要使用的是<keep-alive>组件,官方对它的介绍也仅是在讲<router-view>组件提及了一下,甚至没讲作用(甚是奇怪,或许是自己没找到)

keep-alive 组件使用的介绍-官网

我们的使用也是简单的,只需要第一步,启用<keep-alive>组件

<template>
  <div id="app">
    <keep-alive>
      <router-view v-if="$route.meta.keepAlive" />
    </keep-alive>
    <router-view v-if="!$route.meta.keepAlive" />
  </div>
</template>

可以看到,是否使用<keep-alive>有一个条件判断,而这个判断是我们配置在routes的元信息里面的

第二步,在需要缓存的页面的routes配置的元信息中将keepAlive设置为true,这个meta字段叫keepAlive是我们自己取的,叫什么都可以

{
	path: 'signup/list',
	name: 'SignUpList',
	component: () => import('pages/SignUpList.vue'),
    meta: { keepAlive: true }
}
两个新问题

简单的两步设置,我们期望的列表页面确实被缓存了,但是产生了两个新问题:一是只缓存了页面数据(可以看到任何网络请求被发起了),没有缓存scroll的位置(直接回到了顶部),二是在当前session内一直被缓存了,重新登录之后也是如此(因为登录仅是业务行为)

没有缓存scroll的位置

使用savedPosition,savedPosition是官方推荐使用的方式,滚动行为-官方

使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。 vue-router 能做到,而且更好,它让你可以自定义路由切换时页面如何滚动。

我们可以在,router/index.js里面这样简单的配置:

export default new VueRouter({
  scrollBehavior (to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition
    } else {
      return { x: 0, y: 0 }
    }
  },
  routes,
  mode: process.env.VUE_ROUTER_MODE,
  base: process.env.VUE_ROUTER_BASE
})

当从详情回到列表页面的scroll的缓存效果也实现了,列表页面在离开时的位置,也就是说bac(),达到了我们的业务需求,但是新问题又来了:底部的tabbar(使用router-link的replace来模拟)没有实现对scroll的缓存,列表页面回到了顶部

这不是vue-router的锅,而是业务场景特殊,但是在切换底部tab的时候我们通常也是要保留原始页面的,这也是合理的需求,这个如何设置是一个问题,不过关于这个问题我们后续讨论。

一直被缓存

有些场景下我们不希望列表页面进入的时候是缓存下的上一个页面,我们希望它重新加载数据

关于使用缓存和不使用缓存的Vue的生命周期钩子是这样的:

第一次进入:beforeRouterEnter ->created->->activated->->deactivated

后续进入时:beforeRouterEnter ->activated->deactivated

也就是说如果这个页面是keep-alive的话,我们可以activated里面判断是否要进行load数据,配合meta信息,我们可以轻松的做到这一点

首先,在meta里面添加一个isUseCache的字段(名字随意)

{
	path: 'signup/list',
	name: 'SignUpList',
	component: () => import('pages/SignUpList.vue'),
	meta: { keepAlive: true, isUseCache: true }
}

第二步,在缓存的页面的activated钩子函数里面判断是否重新加载数据

activated () {
	if (!this.$route.meta.isUseCache) {
		// 重新加载数据,包含里重置分页,设空原有数据等操作
		this.reloadData()
	}
},

第三步,通过路由守卫,来动态地修改列表页面的isUseCache状态,我们的业务是从某些页面进入列表页面需要要求它刷新,所以可以通过beforeRouteLeave (to, from, next)这个钩子函数来实现这个效果,如果to是列表页面的话,将to的meta的isUseCache设置为false即可。代码如下:

beforeRouteLeave (to, from, next) {
	if (to.name === 'SignUpList') {
		to.meta.isUseCache = false
	}
	next()
},

以上一套组合拳下来,就基本实现了后退缓存这一需求,只是对于第二个问题,存在部分的代码侵入,不过这也是属于业务的一部分

其它奇怪的内容

从一个keepAlive的页面进入另一个,后者是会刷新的…

参考文章:

vue中前进刷新、后退缓存-简书-白小白大白白

Vue Router 中,`scrollBehavior` 是一个非常有用的功能,用于控制页面切换时的滚动行为。如果发现 `scrollBehavior` 配置无效,可以从以下几个方面排查并修复问题: ### 1. 确保正确配置 scrollBehavior Vue Router 的 `scrollBehavior` 应该在创建路由实例时进行配置,并且返回一个对象来定义滚动位置的行为。例如: ```javascript const router = new VueRouter({ routes: [...], scrollBehavior(to, from, savedPosition) { // 始终滚动到顶部 return { x: 0, y: 0 }; }, }); ``` 如果使用的是 Vue 3 `vue-router@4`,确保你的路由实例是通过 `createRouter` `createWebHistory`(或 `createWebHashHistory`)创建的,并同样提供 `scrollBehavior` 函数 [^1]。 ### 2. 检查是否被异步组件或动态导入影响 如果你的路由组件是异步加载的(例如使用了 `() => import('...')`),可能需要等待组件加载完成后再执行滚动操作。可以通过在 `nextTick` 或组件的 `onMounted` 生命周期钩子中调用 `router.app.$nextTick` 来延迟滚动行为的执行 [^2]。 例如,在 Vue 3 Composition API 中可以这样处理: ```javascript import { useRoute, useRouter } from 'vue-router'; import { onMounted } from 'vue'; export default { setup() { const route = useRoute(); const router = useRouter(); onMounted(() => { router.app.$nextTick(() => { window.scrollTo({ top: 0, behavior: 'smooth' }); }); }); return {}; }, }; ``` ### 3. 处理浏览器缓存历史记录恢复 当用户点击“后退”或“前进”按钮时,浏览器可能会尝试恢复之前的状态(包括滚动位置)。此时 `scrollBehavior` 的第三个参数 `savedPosition` 将会包含上一次的位置信息。你可以选择忽略它并始终返回固定值,或者根据业务逻辑有条件地使用它: ```javascript scrollBehavior(to, from, savedPosition) { if (savedPosition) { return savedPosition; } else { return { x: 0, y: 0 }; } } ``` ### 4. 使用自定义指令强制滚动 如果上述方法仍然无法解决问题,可以考虑使用 Vue 自定义指令来监听路由变化并在每次导航完成后手动触发滚动行为: ```javascript app.directive('scroll-to-top', { mounted(el, binding, vnode, prevVnode) { const router = vnode.appContext.config.globalProperties.$router; router.afterEach((to, from) => { window.scrollTo({ top: 0, behavior: 'smooth' }); }); }, }); ``` 然后在 App.vue 或主布局组件中应用这个指令: ```html <template> <div v-scroll-to-top> <!-- Your content here --> </div> </template> ``` ### 5. 检查 CSS 样式对滚动的影响 有时候,某些 CSS 样式(如 `overflow: hidden`、`position: fixed` 等)会影响窗口的默认滚动行为。请检查页面结构以及全局样式规则,确保没有意外阻止了页面滚动。 ### 6. 浏览器兼容性限制 最后,确认你所使用的浏览器支持 `window.scrollTo` 方法及其选项(如 `behavior: 'smooth'`)。虽然大多数现代浏览器都支持这些特性,但在一些旧版本浏览器中可能存在兼容性问题。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值