先来阐述一下需求

首先我拥有一个详情页面 B 用户可以进入到页面 B 之中分享出去页面 B 但别的用户通过地址直接进入页面 B 这时候 如果用户按了一下返回按钮 需要返回主页

很明显这个需求其实是有点违背 Web 的一些准则的。用户对应他的路由堆栈应该是有一定的掌控和知情的,这个有点破坏的嫌疑,但是没办法,有这个需求。

解决方案

我的大致思路是利用检测popstate这个事件来进行手动路由挑战,但是由于用户刚进入页面,是没有路由栈的,也就是意味着不能监听这个事件,那么我们就手动给用户加个堆栈项,要注意的是加的堆栈项地址要和现在的页面相等,不然就会发现页面跳转了。

微信的弱智坑

情况1 用户第一次打开页面B > 进入生命周期 > 加个路由堆栈项 > 开始监听Popstate 情况2 用户后续打开页面B > 进入生命周期 > 加个路由堆栈项 > 页面刷新??? > 在进入周期 > 开始监听popstate

发现区别了没有,当用户第二次或者后续打开这个页面,会导致页面刷新。

所以我设置了history的length需求等于2,让页面第二次进入到时候再加个路由堆栈项,否则是无法成功监听到的。

其实具体原理我也没搞明白,为什么第二次进入,不加堆栈项监听就无效,不过我也不愿意进一步了,主要这需求我就觉得不太正常,微信也有坑,鬼知道微信到底干了什么事。有效能用就是了。我个人觉得还是微信的臭锅。

export default {
  beforeRouteEnter(to, form, next) {
    // 这里的判断必须为2 不能为1
    if (window.history.length <= 2 && form.matched.length <= 0) {
      next(vm => {
        pushHistory()
        function pushHistory() {
          var state = {
            title: 'title',
            url: '#' + window.location.href.split('#')[1]
          }
          window.history.pushState(
            state,
            'title',
            '#' + window.location.href.split('#')[1]
          )
        }
        localStorage.setItem('needBack', '1')
        vm.listenBack()
      })
    } else {
      next(vm => {
        vm.listenBack()
      })
    }
  },
  methods: {
    listenBack() {
      this.timer = setTimeout(() => {
        let needBack = localStorage.getItem('needBack')
        if (needBack) {
          localStorage.removeItem('needBack')
          const back = () => {
            this.$router.push({ name: 'nHome' })
            window.removeEventListener('popstate', back, false)
          }
          window.addEventListener('popstate', back, false)
        }
        return
      }, 500)
    }
  }
}

缺点

当用户返回 指定页面后 再按一次会返回页面B 因为这个堆栈项其实依旧存活在堆栈之中,并没有被消耗掉。这是不完美的一点。本身history API 也是不支持删除路由堆栈的。