在移动web开发过程中,遇到这样一个问题:返回父页面时,父页面会自动刷新。这个机制在某种场景下很好用,但在列表页与详情页之间跳转时就会导致一个很不好的用户体验。那就是详情页回到列表页时会导致页面刷新,列表页又重置为第一页的数据。
要解决这个问题,第一时间想到的就是有没有方法禁用掉浏览器自身的刷新机制,把刷新禁用了,从详情页返回列表页自然就停留在之前访问的位置了。可惜百度了一番,暂未查到如何禁用浏览器的自动刷新功能,该方案只能放弃!
既然没有禁用浏览器刷新的这条捷径,那就只能靠代码来手动实现了。要实现定位到之前点击的那一条所在位置,最重要的是以下两步操作:
- 1、记录跳转之前列表的页码数。比如我们一页显示10条数据,我们点击的是第3页的第24条数据,那我们要把页码数3记录下来。我们要定位到当前24条数据所在位置,我们至少得从后台请求3页共30条数据过来,否则数据都没有,后续就根本不用想了。
- 2、记录列表页中滚动条当前滚动的距离。我们想要定位到之前点击的位置,就需要通过此距离让页面重新滚动到对应的位置上。
这两条数据我们可以将其保存到本地缓存中,可选择的有sessionStorage跟localStorage。这里选择sessionStorage比较好,因为sessionStorage会在会话结束之后自动清理掉,会话结束了,我们也不需要这两条记录了。
移动端列表页的分页操作一般都是通过下拉重置为第一页,上拉加载下一页来进行。要实现下拉刷新,上拉加载功能可选择的插件有很多,我这里使用的是mescroll.js,所以就拿它来举例了。注意:下面开始贴代码啦!
点击列表中的某一项,记录当前列表的页码数与滚动距离
在每一项的点击事件里去记录我们需要的两条数据。无论是页码数还是滚动距离,都可以通过mescroll对象拿到,不清楚的同学可以百度搜索mescroll查看文档。
// 点击查看商品详情
tapPostDetail(post) {
// 记录当前页码数page
sessionStorage.setItem("page", this.mescroll.options.up.page.num);
// 记录mescroll滚动的距离
sessionStorage.setItem("preScrollY", this.mescroll.preScrollY);
// 记录完成后进行页面跳转
location.href = './detail.html'
}
改写上拉与下拉操作的回调方法
首先贴出mescroll对象的初始化代码,这里我们需要关注的是upCallback回调方法,不管是第一次进入列表页去初始化mescroll对象,还是从详情页回到列表页因为页面刷新导致的mescroll重新初始化,mescroll都会自动触发upCallback回调方法去查询数据。所以我们后续的逻辑都写在upCallback回调方法中。
initMescroll() {
this.mescroll = new MeScroll(this.$refs.mescroll, {
down: {
auto: false,
callback: this.upCallback // 手势操作完成的回调
},
up: {
callback: this.upCallback // 手势操作完成的回调
}
});
}
在upCallBack回调方法中我们真正去查询数据使用的是getDataList方法,该方法接收两个参数,第一个参数为第一条数据在数据库中的索引值;第二个参数为每一页的条数。这里我们就需要改写代码了。因为我们规定一页是10条数据,之前我们都是固定传10 给第二个参数。但这里我们需要判断是否记录了页码数,然后根据页码数 * 10 来获得我们这次需要查询的数据条数。同样是举个栗子,如果我们点击的是第3页的第24条数据,那我们先前记录的页码数就是3,这里我们要传给getDataList方法的第二个参数就是3 * 10 = 30,我们需要获取30条数据。
当数据查询接口请求成功之后,调用mescroll提供的scrollTo方法来滚动到对应的位置。到这里,我们要实现的功能完成了。
upCallback(page) {
// 当下拉重置到第一页时,清空数组数据
if (page.num == 1) {
this.dataList = [];
}
// 第一步:获取下一页第一条数据的索引值
var idx = this.dataList.length || 0;
let pageCount = 10;
let sessPage = sessionStorage.getItem("page");
// 判断缓存中存不存在page, 如果存在则获取之前page*10 的 数据
if (sessPage) {
pageCount = Number(sessPage) * 10;
}
// 发起请求
this.getDataList(idx, pageCount)
.then(res => {
// 获取到最新的数据
var arr = res.data || [];
this.dataList = this.dataList.concat(arr);
this.mescroll.endSuccess(arr.length);
this.$nextTick(() => {
// 判断缓存中有没有preScrollY,有则取出来,滚动到对应的位置
let preScrollY = sessionStorage.getItem("preScrollY");
if (preScrollY) {
this.mescroll.scrollTo(Number(preScrollY), 0);
}
// 清除session里面的缓存
sessionStorage.removeItem("page");
sessionStorage.removeItem("preScrollY");
});
})
.catch(err => {
//联网失败的回调,隐藏下拉刷新和上拉加载的状态;
this.mescroll.endErr();
});
},
虽然返回列表页定位功能实现了,但这里还存在一个bug。当用户没有关闭浏览器结束整个会话的时候,我们存在sessionStorage里的数据是没有清除掉的。这就导致了用户从某个入口再次进入到列表页时会因为之前的记录而直接定位到列表中的某一项处,显然这不是我们想要的。按正常的操作逻辑用户重新进入列表页应该还是从第一条数据开始浏览。也就是说在用户未关闭浏览器的情况下我们需要一个合适的时机去清除掉我们记录的页码数跟滚动距离。
因为是移动web项目,我们没办法在列表页去监听安卓的返回键来清除记录。如果我们的列表页是通过首页的一个入口进入的,我最开始想到的便是在首页那个入口的点击事件里先做清除记录的操作再进行页面跳转。但如果以后有多个页面都有进入列表页的入口时我们该怎么办呢,难道每个页面的列表入口处都去执行该操作?这样做后续维护麻烦,每多一个列表页的入口,就要copy一份代码,稍有遗漏便容易出现bug。
最后想到的还是在当前列表页的upCallback回调方法中去操作。每次请求完列表数据后都执行清除记录的操作。
完美收工。
来源:oschina
链接:https://my.oschina.net/cc4zj/blog/3137859