0%

app内的多步后退

背景

最近在app中开发H5页面碰到了这样的问题:
当我们一直在自己的项目中时我们可以很好的管理我们的history,当我们不希望用户后退到一个页面时我们可以使用replace来替换掉这个页面,这样,当用户点击后退按钮时就不会回到这个页面,比较常见的就是用户在提交表单后会跳转到一个成功页面,这时用户点击后退我们会回退到提交页面,这样可能会造成重复提交,如果使用replace代替push就不会出现这样的问题,但是这种方法依赖于在提交表单页面使用replace,如果提交表单的页面不是我们自己的页面,那么我们并不可预期其他页面会使用什么方式来跳转到我们的页面,这时我们就需要找一些方法来实现类似于replace的效果。
借助history api我们可以操作浏览器的历史记录来实现这个效果。


pushState & replaceState

MDN上关于这两个方法的介绍

  • pushState
    pushState是H5中新加的api,它的作用是向history的记录中添加一个新的记录。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    ahistory.length
    // 2
    history.pushState(null, 'a', null)
    // undefined
    history.length
    // 3
    history.pushState(null, 'a', null)
    // undefined
    history.length
    // 4
    我们可以发现每次我们执行pushState时都会令history的长度增加1,但是需要注意的是,它只会操作history而不会改变页面本身也就是说我们执行这个操作的时候页面不会发生变化,只有history和地址栏会发生变化(地址栏的变化必须同源)
  • replaceState
    replaceState与pushState类似,但是并不会更改history长度。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
      history.length
    //2
    history.replaceState(null, '', null)
    //undefined
    history.replaceState(null, '', null)
    //undefined
    history.length
    //2
    ```

    - - - -

    ### popstate
    > [MDN关于popstate事件的介绍](https://developer.mozilla.org/zh-CN/docs/Web/Events/popstate)
    `popstate`事件是H5中新加的api,它的作用就是在一定条件下浏览器发生后退时触发。
    但是它的表现非常特殊,可以说它是与旧版的historty分离的,它只可以搭配`pushState`和`replaceState`使用,也就是说我们通过a标签和`window.location.href`跳转再点击后退时无法触发这个事件的。
    需要注意的是在某些浏览器中(比如chrome)时会在加载后直接触发`popState`事件。

    - - - -

    ### 解决方案
    基于以上的几个方法我们可以发现,虽然我们没有办法让浏览器少添加一条history记录,但是我们可以监听到浏览器的后退操作然后手动后退更多的记录,这样我们就可以回到我们想回到的位置了。
    ```js
    (function (window, location) {
    history.replaceState(null, document.title, location.pathname + "#stealingyourhistory");
    history.pushState(null, document.title, location.pathname);
    window.addEventListener("popstate", function () {
    if (location.hash === "#stealingyourhistory") {
    if (/iPhone|iPad|ios/gi.test(navigator.userAgent)) {
    alert(history.length)
    history.go(-(window.history.length - 2) >= 0 ? -1 : -(window.history.length - 2));
    } else {
    history.go(-(window.history.length - 2) >= 0 ? -1 : -(window.history.length - 2));
    }
    }
    }, false);
    }(window, location));
    在这个方法中我们手动为浏览器添加一条记录,并且加上一个hash值来标记我们需要多步回退的页面,在我们执行完replaceStatepushState后,我们的地址栏是没有变化的,但是,当用户点击后退按钮后,就会返回带有hash的页面,同时触发了popstate事件,然后就可以执行我们自己的操作了。
    在触发了我们设置的条件后,我希望用户跳转到一级页面,所以我们go(-(history.length - 2)),之所以-2是因为我们手动添加了一条记录,在后退时我们已经向后跳转一次了,所以此处是-2。

#前端/app