手机上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;

 

posted @ 2023-07-26 15:35  jack0424  阅读(589)  评论(0编辑  收藏  举报