背景:
对于一般采用同样的技术栈开发的多页面应用来说,可能遇到的状况如下:
- h5上拉刷新来实现分页,当有很多页的话,点击列表某一页去详细,然后从详情返回上一页,可能刷新上一页,位置不能保持,体验不好
- 列表使用a链接过去的,详情使用window.history.go(-1)返回,有些浏览器不刷新上一页(ios中safari,UC等),有些页面刷新上一页(ios中微信等)
- 有说使用单页的话,可以保持。但是之前用过angular1.X来实现单页,返回貌似也有这个问题(重新执行了列表js),最近在github看到有用vue实现了这个的效果:https://github.com/lzxb/vue-cnode?from=xitu
- 有说列表用window.open,详情用window.history.go(-1),h5实践了,不可以,见上一篇。
- 之前还想过用iframe标签,但是这只适合用于iframe嵌入的页面可进入的下级页面有限,不会再回到列表页面,或者就是一个预览性的小页面。
实际项目中遇到的问题
基于以上背景中的一些方法,除了提到的一些弊端,还只适合于简单的从详情页到列表页不需要更新列表页数据的情况,如果是需要更新列表页数据就需要考虑怎样把要更新的某一条数据的标志传到列表页,列表页再通过ajax请求最新的数据。
在实际中遇到的项目是原来的php项目用vue重构,列表页等部分页面是vue单页应用,详情页因为考虑seo的问题依然是php页面,这就导致两个完全不同的技术栈的页面之间是完全独立的,背景中提到的打开新窗口的页面对于pc端和安卓手机的主流浏览器都是可行的,但是对于微信浏览器和iphone中的大部分国内浏览器都是默认不执行新窗口打开的。本来想用直接关掉新窗口重现列表页的方法还是不够好,特别是对于微信浏览器这种手机端比较大的流量入口来说,无论怎样都会刷新列表页,根本达不到我们想要的效果。
因此,后面才想着把当点击列表页的时候,把列表页整个数据都存在客户端,当再次返回到列表页的时候,通过判断是否存在数据来确定是否需要加载新的数据,如果有数据直接将页面显示出来,然后定位到之前保存的scrolltop值得位置。
这种方法当然解决了手机各个浏览器直接新窗口打开的兼容性问题,唯一的弊端就在于可能要保存的数据量有点大,并且数据保存在客户端可能存在的一些安全风险。
还有当时遗留的怎样判断是从详情页返回的问题,后来在github上面看到设置锚点的方法,大体的思路跟我想的一致。只不过我的是vue里面,因此我是把整个data数据转成字符串保存了下来。锚点的具体实现方式是在列表页滚动的时候记录页面的scroll-top值,然后把scroll-up的值设置锚点如(#scrollY = scroll-top)这样如果是返回的页面就可以在列表页获取到hash,hash存在就可以判断是从详情页返回的页面,如果是从其他页面通过链接进入的就直接新请求数据,更新列表页。
一些说明
- 列表滚动使用锚点获取scrollTop值,标志是否返回了列表
- localStorage有过期时间,不用sessionStorage主要是因为有些安卓机的浏览器,跳页面会获取不到sessionStorage值,比如UA:Mozilla/5.0 (Linux; U; Android 5.0.2; zh-CN; Redmi Note 2 Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/40.0.2214.89 UCBrowser/11.2.0.880 Mobile Safari/537.36
- 只有返回列表才执行本地数据,index.html到list.html会重新请求数据。
- 缺点就是会将列表页的数据都记录下来,数据的风险性。
https://luchanan.github.io/detail2list/index.html 类似的github上的一个项目示例,非常感谢分享啦
其他的联想
可能的解决方案:
- 改交互方式,把上拉刷新改为类似美团美食h5那样,手动点下一页,稍微简单些,这样每次都只记录页码和滚动的位置就好了(但是一般产品不会同意)
- 列表记录当前页码,到顶部请求上一页,到底部请求下一页。(好麻烦,先放弃,位置也不准确了)这种就是数据请求很混乱。
最后附上自己测试过的vue页面中使用的代码: