后退刷新

前段时间遇到了移动端后退更新的业务需求,踩了坑当然要总结了。

后退更新的坑在于浏览器后退时,缓存文件的使用,JS 代码的执行。

一、使用了缓存文件,从缓存方向解决

直接禁用缓存,如meta禁用缓存(不可靠),在响应的 header 里面禁用换缓存

(手机端)此方法行不通,因为有些浏览器会忽略这个头部

二、bfcache 与 pageshow

bfcache,即back-forward cache,可称为“往返缓存”,可以在用户使用浏览器的“后退”和“前进”按钮时加快页面的转换速度。实际上是将整个页面都保存在内存里,如果页面位于bfcache中,那么再次打开该页面就不会触发onload事件。“后退”不光意味着html/js/css/接口等动静态资源不会重新请求,连JS也不会重新执行。那么发生后退时浏览器直接把“离开时”的页面状态展示给用户。

pageshow事件,这个事件在页面显示时触发,无论页面是否来自bfcache。在重新加载的页面中,pageshow会在load事件触发后触发;而对于bfcache中的页面,pageshow会在页面状态完全恢复的那一刻触发。

pagehide事件,该事件会在浏览器卸载页面的时候触发,而且是在unload事件之前触发。

pageshow事件和pagehide事件的event对象还包含一个名为persisted的布尔值属性。

对于pageshow事件,如果页面是从bfcache中加载的,则这个属性的值为true;否则,这个属性的值为false。

对于pagehide事件,如果页面在卸载之后被保存在bfcache中,则这个属性的值为true;否则,这个属性的值为false。

IOS

根据以上特性,Safari 支持 bfcache

if (isiOS) {
    window.addEventListener('pagehide', function(e) {
        var $body = $(document.body);
        $body.children().remove();

        setTimeout(function() {
            $body.append("<script type='text/javascript'>window.location.reload();<\/script>");
        });
    });
}

既然后退回来时 JS 处于当时离开的状态,那么上面的答案就是监测到进入下一个页面时(pagehide事件),创建一个 script 标签(里面执行刷新代码)添加在html内,并且因为 JS 没有被执行处于冻结状态,等下次回来就可以继续执行了。但是一般情况下会被当前页面执行,所以这个地方用 setTimeout 就很有灵性了。

Stack Overflow 答案地址

安卓

根据 pageshow 直接用

window.addEventListener('pageshow', function(evt){
    setTimeout(function(){
        if(evt.persisted){
            location.reload(true);
        }
    });
});

但是,安卓webview,包括安卓微信,都存在后退不会重新请求页面的问题,无论页面是否禁用缓存。上面的pageshow/pagehide方案也都失效。

大部分方法都是存储服务器版本号然后与本地版本号进行对比,还有另一种方法虽然有缺陷但简单的方法

if(window.name != "bencalie"){
    location.reload();
    window.name = "bencalie";
}else{
    window.name = "";
}

后退时,因为会执行 JS,那我就依靠 window.name 强制刷新,缺点就是第一次进入页面时也会被强制刷新