这里不是浏览器的「返回」按钮,我们没办法修改它的行为。
而是网页代码中的「返回」按钮,我们可以定义它的行为。
这种需要返回上层页面的按钮,在本文中,称之为「返回」按钮。
这3种,都可以实现页面跳转,对于用户体验也是有差异的。
「返回」按钮,做好用户体验,挺难的。这里罗列一些容易想到的、但不完美的方案。
存在的问题:
其实,如果用back实现「返回」按钮,这个按钮元素会有点多余,因为它与浏览器原生的「返回」能力一样。
这种方式解决了back导致的2个问题,但并不完美。
我解释一下。比如有个初始页面H,用户从初始页面H跳转到了列表页A,用户通过点击列表页A里面的详情Ax链接(x代表一个正整数,列表页通常有多个详情链接),可以进入详情页Ax。在详情页Ax中,可以点网页「返回」按钮,回到列表页A。
当用户在列表页A和详情页Ax之间多次通过详情Ax链接和网页「返回」按钮来回切换时,页面浏览记录已经累积很多了,用户若想通过浏览器原生「返回」按钮,再返回初始页面H,是需要按很多次返回的。
但用户没有这个耐心。
所以你不得不在列表页A增加一个网页「返回」按钮,用于跳转初始页面H。这就诞生了新的问题:
除此之外,我想强调一句:
剥夺用户使用原生「返回」按钮的权利,不是一件好事。
尤其是对于安卓端用户,重度依赖原生「返回」操作(在屏幕边缘左滑或右滑)。网页打破了他们的操作习惯,只能表明网页用户体验做的不够好。
这里,我想先提出「页面层级」的概念。
假设网站有这样的结构:
它是一个树状结构,每个页面、模块划分非常清晰。
什么是页面层级?
同一层子结点,称之为同一个「页面层级」。(例如图中模块A、B、C就是同一层级)
这样,页面整体跳转逻辑,是非常清晰的,对于用户而言,也容易理解你的逻辑。
产品原则的目标:让浏览器的历史记录栈与网页结构保持一致:
而浏览器原生的「返回」,正是使浏览器的历史记录栈回退1个。这样两种「返回」就归一了。
这件就解决了「3.2方案二」中的问题,达到这样的效果:
但网页「返回」按钮还有个问题必须解决:若浏览器当前历史记录栈为空,或历史记录栈的上个页面并非该网页的页面,点「返回」,应该也能返回它的父页面。
现在我告诉你,这个技术难点,是有解的!
难点:如何判断历史记录栈的上个页面,是不是我的父页面。
问题:浏览器基于安全性,不允许你读取历史记录栈。
跳转时的「标识」,刚好可以用history.pushState()中的state来实现。
只要是内部跳转,都封装一个统一的组件。该组件允许定义跳转目的地,而且会在state中携带「标识」(如果你的网页有带自定义state的诉求,则还需要在该组件中组装一下参数中的state和「标识」,变成新的state)。
获取当前页面的state,如果包含了「标识」,则直接history.back();否则,用history.replaceState(注意replace时不用带「标识」)。
实际使用中,发现一个问题,我直接举真实案例。
我的五子棋,联机对战模式,页面分为3个层级:首页、对战房间、单机演练。按照如下流程操作:
为了解决这个情况,我做了兼容处理:
如果当前页面state没「标识」,如果当前浏览器历史记录栈长度为1,直接replace是没问题的,不会出现上述问题;但如果当前浏览器历史记录栈长度大于1,我调用replace后,需要连续调用一次push和一次back,目的是清空浏览器「前进」的历史记录栈。
这是LinkButton逻辑,其中back参数,true表示是返回按钮,false表示是跳转按钮。我的state中「标识」叫做keepSession。
如果你的父页面和子页面,不是同一套前端代码,而是两套前端代码。也就是说,它整体不是单页面应用(SPA),而是多页面应用(MPA),该怎么办呢?
只要你的页面里,没有「返回」按钮,那啥事都没有
如果你的页面,不追求移动端的极致用户体验,那也没啥事,PC端用户对原生「返回」的依赖没那么重,你想剥夺就剥夺吧
而我要做移动端页面,有些情况下,原生「返回」是无法返回上一层级的(例如用户直接从url进入了第2层级,原生返回只能关闭页面,不能返回第1层级),所以我在网页加了「返回」按钮。与此同时,我还没剥夺用户使用原生「返回」的权利。总算是完成了令我满意的「返回」