移动端react/H5开发通过JS捕捉安卓物理返回键的实践

业务需求及要求

一共5个页面,页面内有导航栏和返回,其中两个页面的返回有特殊要求,而且物理返回键点击动作要和导航中的返回完全一致。

  1. 首页——项目一打开在首页,点击某个元素跳转到列表页
  2. 列表页——可以点击新增跳到表单编辑页,点击草稿可以跳到草稿箱,点击每个图可以跳到详情页;点返回回到首页
  3. 详情页——点击编辑可以跳到表单编辑页;
  4. 表单编辑页——点击保存跳到草稿箱,点击提交跳到列表页;
  5. 草稿箱——点击每个图片进入表单编辑页;点返回回到列表页

解决过程

网查了一下,花了大半天时间尝试了以下方法

1,监听popstate事件

可以通过监听popstate事件来感知也变化,但不能保证一定是点了返回键触发的,也就是正常的路由跳转全被监听了。这个类型的版本有好几个,都存在此问题。

window.addEventListener("popstate", function(e) { 
    window.location.replace(url)//点击返回键时,需要返回的页面
}, false);

2,封装的XBack来阻止安卓返回键

找到了XBack代码片段,发现放在全局初始化就会把任何情况下的返回键屏蔽掉,如果放在导航栏组件里就可以成功捕捉到并阻止物理返回键,但这种情况下跳转一下容易触发好几十次的路由跳转,且每次不尽相同,最终的路径也不一定是想要的,也懒得用防抖节流再试了。

let XBack = {};
const {location,history} = window;
(function(XBack) {
    XBack.STATE = 'x - back';
    XBack.element ={};

    XBack.onPopState = function(event) {
        event.state === XBack.STATE && XBack.fire();
        XBack.record(XBack.STATE); //初始化事件时,push一下
    };

    XBack.record = function(state) {
        history.pushState(state, null, location.href);
    };

    XBack.fire = function() {
        var event = document.createEvent('Events');
        event.initEvent(XBack.STATE, false, false);
        XBack.element.dispatchEvent(event);
    };

    XBack.listen = function(listener) {
        XBack.element.addEventListener(XBack.STATE, listener, false);
    };

    XBack.init = function() {
        XBack.element = document.createElement('span');
        window.addEventListener('popstate', XBack.onPopState);
        XBack.record(XBack.STATE);
    };

})(XBack); // 引入这段js文件

XBack.init();
XBack.listen(function() {
    console.log('----------物理键返回', backUrl,)
    if(backUrl){
        // window.history.pushState("","",'/#'+backUrl);
        // window.history.pushState("","",'http://'+window.location.host+'/#'+backUrl);
        // window.location.href='http://'+window.location.host+'/#'+backUrl;
        window.location.replace('/#'+backUrl)
    }else{
        // window.history.back()
    }

});

3,其他方法

  1. 通过在跳转的目标页面进行检测拦截来实现。经分析,只需在form页面判断,如果来自草稿箱,则跳到列表页;如果来自列表页则跳到App。但问题是,这两个页面需要返回到不同的页面,但无法区分来源。
  2. 打印history对象,企图从中找到些路径先后关系的信息,无果,其中action的值也仅能判断出是跳过来的还是退回来的,对草稿箱和列表页都能退回的form页,仍然无法区分。
  3. 曾尝试过一个方法:为了区分物理返回键和导航栏返回键,在导航栏返回时将一个外部变量标记,再用setTimeout在200ms后清除标记。在此期间通过监听popstate和判断标记来区分是否物理按键。但这样需要把所有跳转都标记出来。结论

 

终极大法

  1. 每个页面加载时,在constructor中将页面标记pageMark写入sessionStorage中:列表页1,详情页2,编辑页3,草稿箱4.
  2. 进入相关页面时先判断history的行为是否后退(props.history.action==='POP'),如果是说明按了返回键(无需判断是物理返回键还是页面内的回退),再根据以下情况判断
    • 在编辑页
      1. 如果是4就说明来自草稿箱,就执行跳转到列表页;
      2. 如果是1就说明来自列表页,执行跳转到首页。
    • 在草稿箱
      1. 如果是1就说明来自列表页,执行跳转到首页。
  3. 例如form页面的constructor
constructor(props) {
    super(props);
    if(props.history.action==='POP'){ // 说明后退了
        const pageMark = sessionStorage.getItem('pageMark');
        if(pageMark==='4'){ // 从草稿箱要回来
            this.props.history.push('/list')
        }
        if(pageMark==='1'){ // 从列表要回来
            this.props.history.push('/')
        }
    }
    sessionStorage.setItem('pageMark','3');
    ……
}

 

你可能感兴趣的:(前端移动开发,前端)