手机上vue页面返回时如何保持原来的位置
1,问题的提出
采用vue做手机评分页面的前端,页面显示多个评分项的分数和总分。
每个评分项有个修改按钮,按下后弹出新的页面,用户填写分数后按提交按钮,则保存数据、关闭页面、回到前一页。
此时,页面上显示的分数和总分会刷新,但是显示的页面位置未保留修改前的位置,而是回到了顶部显示。
这种情况如果页面上的分数项多的话,每次填完一项分数,就要下拉一次页面才能回到原来修改分数的位置,继续修改下面的分数,对用户来说非常不便。
那么,有没有办法返回前一页时显示的是离开前的位置呢?
2,走过的弯路
从网上查阅资料,提供了以下思路:离开页面时记住页面位置,回到页面时将位置设置回原来的位置。
结果实践证明是不能成功。但严格来说,可以成功50%,上面前半句可行,后半句不可行。
以下是采用beforeRouteLeave事件记住当前位置,可以取得当前页面的垂直偏移位置:
beforeRouteLeave(to, from, next) { this.$alert(`滚动位置:${document.documentElement.scrollTop} || ${document.body.scrollTop}`); sessionStorage.setItem('scrollTop', document.documentElement.scrollTop || document.body.scrollTop); next(); },
但是,在返回页面的mounted事件中设置scrollTop,却始终是无效的。
以下是我曾经的尝试。无论是设置document.body.scrollTop,还是设置document.documentElement.scrollTop,还是设置window.pageYOffset,统统无效。
通过this.$nextTick或setTimeout延时设置也无效。
通过页面主div设置其scrollTop,也一样无效。
mounted() { // let a= sessionStorage.getItem('scrollTop'); // // this.$alert(a+",mounted,"+document.documentElement.scrollTop); // // this.$alert("1"); // document.body.scrollTop = 5000; // document.documentElement.scrollTop = 4000; // window.pageYOffset = 7000; // // let that=this; // this.$nextTick(()=>{ // setTimeout(()=> { // let mainDiv = that.$refs.mainDiv; // mainDiv.scrollTop = 1000; // // document.body.scrollTop = 500; // // document.documentElement.scrollTop = 400; // // window.pageYOffset = 700; // that.$message.info("$nextTick,"+document.body.scrollTop); // }, 13) // // }) // this.$message.info(a+",mounted,"+document.body.scrollTop); },
3,有效的方案
显然,不是上述方案不正确,而是上述方案不适用于vue前端。
经过查找资料,以下文章(以下简称原文)给出了可行方案:
https://blog.csdn.net/sinat_41694829/article/details/116018702
上述方案的思路是2点:用keepAlive缓存页面及记忆上次浏览位置,以及用activated刷新页面数据。
具体来说,包含以下5步:
第一步,修改app.vue
对于需缓存的页面,用<keep-alive>标签包裹。
<template> <div id="app"> <keep-alive> <router-view v-if="isRouterAlive && $route.meta.keepAlive"></router-view> </keep-alive> <router-view v-if="isRouterAlive && !$route.meta.keepAlive"></router-view> </div> </template>
第二步,修改router\index.js
将需要缓存的页面,在路径特性中申明keepAlive属性和 isNeedRefresh 属性。
{ path: '/m_TargScore_Score/:openId/:months', name: 'm_TargScore_Score', component: m_TargScore_Score, meta: { keepAlive: true,
isNeedRefresh: true
}
},
第三步,判断页面是否首次创建
mounted() { // 只有第一次进入或者刷新页面后才会执行此钩子函数,使用keep-alive后(2+次)进入不会再执行此钩子函数 // isFirstEnter 用来标记是否第一次进入该页面,以防止用户在详情页手动刷新页面,回到该页面后不再请求数据 this.isFirstEnter = true; },
第四步,判断页面是否来源于详情子页面
beforeRouteEnter (to, from, next) { // 利用路由元信息中的 meta 字段设置变量,方便在各个位置获取。这就是为什么在 meta 中定义 isNeedRefresh。 // 当从详情页返回时,将 isNeedRefresh 设为 false,表示不刷新数据 if (from.name === 'm_PerfScore') { to.meta.isNeedRefresh = false } else { to.meta.isNeedRefresh = true } next() },
第五步,修改缓存页面的activated事件
在该事件中判断是否需要刷新页面数据。
activated() {
if (this.$route.meta.isNeedRefresh || this.isFirstEnter) {
// 如果 isNeedRefresh 是 true,表明需要获取新数据,否则就不再请求,直接使用缓存的数据
this.initTargData();
}
// 恢复成默认的 false,避免 isFirstEnter 一直是 true,导致重复获取数据
this.isFirstEnter = false
// 恢复成默认的 true,避免 isNeedRefresh 一直是 false,导致下次无法获取数据
this.$route.meta.isNeedRefresh = true
},
原文还提出要在Router中加上 scrollBehavior事件,但本人实测以上五步就够了。
4,总结
原文中指出,本方案的关键在于vue 的生命周期和路由钩子的相关知识点。
不使用 keep-alive 时
beforeRouteEnter --> created --> mounted --> destroyed
使用 keep-alive 时
beforeRouteEnter --> created --> mounted --> activated --> deactivated
再次进入缓存的页面,只会触发beforeRouteEnter -->activated --> deactivated 。created和mounted不会再执行。
因此对于缓存的页面,刷新数据操作要放在activated 事件中执行。
在此感谢原文作者!
另外需要注意:如果data中的数据项采用了固定方式的引用,那么必须在刷新数据前重新赋值!
data() { return { openId: this.$route.params.openId, 。。。 。。。 initTargData() { this.openId = this.$route.params.openId;