通过vue中的历史api拦截浏览器的前进后退按钮事件。
让我们来看看MDN文档中对这个api的解释:
也就是说,当我们点击浏览器的前进后退按钮或者执行js中的history.back()和history.forward()时,就会触发popstate事件。
那么这个api和vueRouter的历史模式有什么关系呢?
我们知道,vue项目实际上是一个单页应用程序,它的一般结构如下:
所以我们在项目中的各种跳转切换页面都不要求html资源,都是在一个html中完成dom切换,也就是说我们可以改变route-view标签页中的内容,但是可以通过浏览器的前进和后退按钮进行页面切换。实现的关键是监听popstate事件,并根据相关信息改变route-view选项卡中的内容。
这里需要介绍另一个api,history.pushstate
还要看MDN文档的介绍:
该api将向浏览器的历史堆栈添加一个状态,例如:
比如你现在在A页,然后从A页跳转到B页,然后在B页执行history.pushState(),此时点击浏览器的后退按钮不会返回A页,仍然会在B页。
两个API的MDN文档,请看一遍:
popstate
history.pushState
所以vueRouter的历史模式简单来说就是监听popstate事件,执行切换显示dom的事件;然后在路由切换的时候执行history.pushState(),然后手动触发切换显示dom的事件。
现在回到题目,如何在vue中拦截浏览器的前进后退按钮?最近刚遇到一个需求,需要这个功能来实现:
有一个列表组件列表:
列表页面有很多数据,每个数据只显示基本信息。然后单击每个项目以显示每个项目的详细信息,并跳转到详细信息页面。
这个需求很常见,实现的方式也有很多种,比如动态路由,但是如果是动态路由的话,当你返回列表页的时候会刷新列表页,没有keep-alive。交互不是很友好,我一般通过组件切换来实现:
列表被v-show隐藏,细节组件被v-if初始化。但是这种写法还有一个问题。当显示细节页面时,不能通过浏览器的后退按钮返回列表页面,因为它们只能在同一个组件和路由中使用自己的后退按钮。为了考虑用户的习惯行为,在细节组件中点击返回按钮时,需要返回列表页面。
结合以上内容,当进入details组件时,该行为被视为进入了一个新的页面,因此有必要在浏览器会话的历史堆栈中添加一个状态:
History.state存储当前浏览器会话的信息,包括什么是前进状态,什么是后退状态。在上面的例子中,我给它添加了一个ID。History.pushState有三个参数,第一个参数是State,第二个参数可以忽略,第三个参数url可选,通过第三个参数指定vueRouter的切换。如果没有传递第三个参数,当前的url将不会改变。
当执行上面的goDetail方法时,将显示细节组件。如果列表页的路由的/list从/home路由跳转,在显示详情页时,点击浏览器的后退按钮,因为已经执行了history.pushState,所以不会返回/home路由,仍然会在/list中路由。
那么,当您在细节组件中时,如何实现单击back按钮隐藏细节组件并显示列表呢?
此时,有必要监控popstate事件。我在history.pushState的时候给状态加了一个logo,以key为id,通过判断是否有这个logo来切换组件:
在显示detail组件时,点击返回按钮,触发popstate事件,history.state返回到history.pushState未执行的状态,即history.state没有id,因此detail组件将被隐藏;此时,再次点击浏览器的前进按钮,history.state将返回到已经执行history.pushState的状态。使用id徽标,将显示详细信息组件。
这说明同一路由下组件的切换是由浏览器的前进和后退控制的。
基于在一个组件中切换一个列表和一条数据的详细需求,对所涉及的两个历史api的理解还有很多不足之处。如果有更好的意见,请指教。
我是一只鸭子,祝你幸福。