bfcache相关

原创 2016年08月31日 13:17:30

前言

让我们来看一种情况:一个列表页面中,预期每次网页载入后,onload()事件都会以ajax方式重新取得最新的列表资料,因此点击后退到该页面,用户应该能立即看到最新的列表资料。然后,在一些浏览器中,发现回到上页时不会自动载入更新后的资料,需要刷新才能重新载入。这是因为:
部分浏览器在后退时不会触发onload事件,這是HTML5世代浏览器新增的特性之一——Back-Forward Cache(简称bfcache)

什么是bfcache

我们熟悉的红本本《JavaScript高级程序设计》有提及bfcache:

bfcache,即back-forward cache,可称为“往返缓存”,可以在用户使用浏览器的“后退”和“前进”按钮时加快页面的转换速度。这个缓存不仅保存页面数据,还保存了DOM和JS的状态,实际上是将整个页面都保存在内存里。如果页面位于bfcache中,那么再次打开该页面就不会触发onload事件

pageshow事件

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

pagehide事件

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

persisted属性

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

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

不同的浏览器在浏览器会在当前窗口“打开”历史纪录中的前一个页面的表现上并不统一,这和浏览器的实现以及页面本身的设置都有关系。

测试

测试代码

线上示例

<!DOCTYPE html>
<html>
<head>
    <title>Page Events</title>
    <meta charset="utf-8">
    <script>
        function appendFunc(msg){
            var d = new Date();
            var li = document.createElement("li");
            li.innerHTML = d.toISOString().substr(14, 9) + " " + msg;
            document.querySelector("ul").appendChild(li);
        }
        window.addEventListener('load', function(){
            appendFunc('Load Event');
            document.querySelector("#changeColor").onclick = function(){
                document.querySelector("a").style.color = "red";
            }
        })
        window.addEventListener('pagehide', function( e ){
            appendFunc("Pagehide Event");
            appendFunc("pagehide persisted is :"  + e.persisted);
        })

        window.addEventListener('pageshow', function( e ){
            appendFunc("Pageshow Event");
            appendFunc("pageshow persisted is :"  + e.persisted);
        })
    </script>
</head>
<body>
<a href="http://www.sina.com.cn/">前往大新浪</a>
<input type="button" id="changeColor" value="变红" />
<ul></ul>
</body>
</html>

测试结果

browser result
IE 11 新载入会触发load和pageshow事件,紅色未保留,没有bfcache
Chrome 52.0.2743.116 新载入会触发load和pageshow事件,紅色未保留,没有bfcache
Opera 39.0 新载入会触发load和pageshow事件,紅色未保留,没有bfcache
Firefox 48.0.2 新载入会触发load和pageshow事件,点前往大新浪时触发pagehide事件,但不存入bfcache,紅色未保留
Safari(iPhone) iOS 9.3.5 新载入会触发load和pageshow事件,点前往大新浪时触发pagehide事件,存入bfcache ,紅色保留
UC 新载入会触发load和pageshow事件,点前往大新浪时触发pagehide事件,存入bfcache ,紅色保留
qq浏览器 新载入会触发load和pageshow事件,点前往大新浪时触发pagehide事件,显示没有存入bfcache ,但是紅色保留,有bfcache但是不支持pagehide事件的persisted属性(存疑)
browser 新载入会触发load和pageshow事件,回上页时不触发任何事件且红色被保留,有bfcache(存疑)

可以看到,Safari、Firefox、UC、qq浏览器、browser都有bfcache。
回到上页时,

  • Safari、UC和qq浏览器都不会触发load;
  • IE、Firefox、Chrome和Opera支持pageshow事件,但是会触发load;
  • browser不触发任何事件。

解决方案

Firefox的开发者文档中提供了一些思路:

  • 页面监听了 unload 或者 beforeunload 事件;
  • 页面设置了 “cache-control: no-store”.
  • 网站使用 HTTPS 同时页面至少满足以下一个条件:
    • “Cache-Control: no-cache”
    • “Pragma: no-cache”
    • 设置请求头 “Expires: 0” 或者 “Expires” 的值为 “Date” 之前的值 (除非 “Cache-Control: max-age=” 也被设置了);
  • 页面在用户前进后退的时候还没有完全加载完或者它有正在进行的网络请求,比如 XMLHttpRequest;
  • 页面正在进行IndexedDB操作;
  • 顶层的页面包含有frame,并且这些frame由于这里列的任何一条原因而不能被缓存;
  • 页面在一个frame内,并且用户在这个frame内跳转到了一个新的网页,这里将被缓存的是新载入的网页

J监听pageshow事件阻止页面进入bfcache

一言不合放代码

window.addEventListener('pageshow', function( e ){
    if (e.persisted) {
        window.location.reload() 
    }
})

Safari、UC、qq浏览器测试通过。但是UC、qq浏览器会先闪过bfcache中的页面,因为pageshow是在load事件触发之后才触发的。browser依然会保留红色,我认为是因为browser回到上页时不触发任何事件。

JS监听pagehide事件阻止页面进入bfcache

一言不合放代码

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

    // 要等到回调函数完成,用户按返回才执行script标签的代码
    setTimeout(function() {
        $body.append("<script type='text/javascript'>window.location.reload();<\/script>");
    });
});

Safari、UC、qq浏览器测试通过。browser依然会保留红色,我认为是因为browser回到上页时不触发任何事件。

给响应添加Cache-Control的header

代码示例如下:
在jsp模板的header部分加入如下的禁用缓存设置:

<head>
<%
  response.setHeader("Cache-Control","no-cache,no-store,must-revalidate");
  response.setHeader("Expires", "0");
  response.setHeader("Pragma","no-cache"); 
%>
</head>

安卓webview cache的解决方案

该方案的前提是浏览器在向server请求页面时,每次都用jsp重新生成html。需要页面本身有禁用缓存的配置。

<!-- 安卓webview 后退强制刷新解决方案 START -->
<jsp:useBean id="now" class="java.util.Date" />
<input type="hidden" id="SERVER_TIME" value="${now.getTime()}"/>
<script>
//每次webview重新打开H5首页,就把server time记录本地存储
var SERVER_TIME = document.getElementById("SERVER_TIME"); 
var REMOTE_VER = SERVER_TIME && SERVER_TIME.value;
if(REMOTE_VER){
    var LOCAL_VER = sessionStorage && sessionStorage.PAGEVERSION;
    if(LOCAL_VER && parseInt(LOCAL_VER) >= parseInt(REMOTE_VER)){
        //说明html是从本地缓存中读取的
        location.reload(true);
    }else{
        //说明html是从server端重新生成的,更新LOCAL_VER
        sessionStorage.PAGEVERSION = REMOTE_VER;
    }
}
</script>
<!-- 安卓webview 后退强制刷新解决方案 END -->

总结

  1. PC浏览器,设置禁用页面缓存header即可实现后退刷新

  2. 支持bfcache/page cache的移动端浏览器,JS监听pageshow/pagehide,在检测到后退时强制刷新

  3. 在前2个方案都不work的情况下,可以在HTML中写入服务端页面生成版本号,与本地存储中的版本号对比判断是否发生了后退并使用缓存中的页面

参考

bfcache往返缓存

Firefox和Opera有一个新特性,名叫“往返缓存”(back-forward cache,或bfcache),可以在用户使用浏览器的“后退”和“前进”按钮时加快页面的转换速度。这个缓存中不仅保存...

解决手机上页面返回但是页面js没有刷新的痛点(BFcache)

一些浏览器中返回按钮是直接使用缓存的,不会执行任何js代码,例如, 在提交的时候将按钮设置为loading状态,如果在提交成功后没有对按钮进行处理,那么返回后按钮依然是loading状态,这就很尴尬了...

页面显示(pageshow)和页面隐藏(pagehide)事件--bfcache

转载自:http://blog.csdn.net/annaleeya/article/details/8278929 Firefox和Opera有一个新特性,名叫“往返缓存”(b...

网传json格式相关jar包

  • 2017年12月08日 12:03
  • 1.25MB
  • 下载

密码学-数字签名相关内容整理

  • 2017年12月01日 21:54
  • 3.85MB
  • 下载

【已解决】Remote Desktop Connection报错 证书或相关链无效

最近换了mac,捅咕了几天,算是把环境基本搭好了,今天记录下。这个无效的问题解决起来很容易,记录下来主要是我觉得在mac上安装微软的这个软件挺有趣的。...
  • Dcatfly
  • Dcatfly
  • 2016年11月07日 21:55
  • 17965

SSM框架相关jar包

  • 2017年11月30日 11:09
  • 24.36MB
  • 下载

云计算架构及相关知识点总结

  • 2017年11月26日 17:57
  • 20.33MB
  • 下载

安卓时间相关的广播(Intent.ACTION_TIME_TICK)

1.Intent.ACTION_TIME_TICK 含义:系统每分钟会发出该广播2.用法: IntentFilter filter = new IntentFilter(); filt...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:bfcache相关
举报原因:
原因补充:

(最多只允许输入30个字)