Vue-router如何实现拦截物理返回键的回退功能

前言

拦截物理返回键的需求其实还是蛮多的,比如,点击返回键关闭弹层,点击返回键避免没有保存就退出,等等。

如何拦截物理返回键这个事,网上有很多搜索结果,但是真正解决问题的文章我硬是没有找到,瞎鸡巴写的倒是很多,所以干脆自行研究了一下,给大家介绍一个方法。

定个需求

现在我的项目中有一个购买商品的弹层,也就是sku,那么,当点击物理会推荐,如果弹层显示着,我就让它隐藏,而且要阻止退回到上一页。

浏览器困境

拦截物理键,原则上是监听popstate事件,然而,现实很残酷,因为popstate事件的意思是路由已经变化完成,也就是说,并没有一种方法真正监听物理键的点击。一旦触发popstate,其实就已经晚了,因为路由已经变化了,你已经无法拦截。

因此,window.addEventListener("popstate", fn)的回调函数fn,是在浏览器回退之后执行,这个回退是浏览器自己执行的,不受任何控制,无论是加上event.preventDefault()还是return false都白扯。

怎么办?

只能是给浏览器跪下,先听从它回退,然后再重新push回来。好在,Vue-router给我们提供了push回来的方法。

好了,我们开始。

方法

在需要关闭sku弹层的组件(必须是路由级组件)里加入:

  beforeRouteLeave(to, from, next) { // 仅适用于本页面不作为H5着陆页的情况
    if (this.skuShow) {
      this.$store.commit('SET_SKU_SHOW', false)
      next(false)
    } else {
      next()
    }
  },

核心代码是next(false)。根据官方说法:

如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。

所以其实也是push回来的道理。

着陆页困境

最后还有一个重要的问题就是,如果你是在项目着陆页(也就是浏览器打开的项目的第一个页)弹了一个sku层,那么这个方案就无效了,道理也很简单,浏览器先回退,有3种可能:

  1. 可以回退,但退出项目了,退到先前打开的其他网站的页面了,这时候再想push回来也白扯了,因为项目都退了,怎么执行push?
  2. 无法回退,既然无法回退,那么就不触发beforeRouteLeave钩子,所以也不能执行代码。
  3. 如果项目是在APP的webview中打开的,一般来讲是不存在第一种情况的,只可能是第二种情况,那么当无法回退的时候,APP会退出webview,也依然不会触发beforeRouteLeave钩子。

所以结论就是:

  1. web端,想要物理回退键只关闭sku,就要另想办法了,而且几乎没办法,至少我是没办法。
  2. 如果是在APP的webview中,办法还是有的,就是APP监听物理返回键,监听到之后,调用js环境的window对象上的一个方法,这个方法的作用是读取vuex中的skuShow的值,如果是true,则APP拦截物理键,方法也把skuShow改为false,如果skuShow本身就是false,则正常操作即可。

当然了,首页就出现弹出层这种交互方式,本身也应该避免,因为首页往往是作为导航用的,应该减弱交互。

你可能感兴趣的:(Vue-router如何实现拦截物理返回键的回退功能)