首先说说为什么会学习这个 pageshow 的起因吧,项目中一个表单页面,需得填写验证码,填写验证码后提交,由于使用的form直接提交,没有使用 AJAX,所以,在出现验证码填写错误的时候,就会跳转到错误提示页,3 秒倒计时后使用 history.back(-1) 的方式返回上一页,这也就出现了一个问题,使用 history.back(-1) 进行后退之后,图形验证码已过期,但是却没有刷新,这样就导致用户重复多次填写验证码,影响用户体验;
发现了问题,首先分析一下:
用户点击浏览器工具栏中的后退按钮,或者移动设备上的返回键时,或者 JS 执行 history.go(-1); 时,浏览器会在当前窗口 打开 历史纪录中的前一个页面。不同的浏览器在“打开”前一个页面的表现上并不统一,这和浏览器的实现以及页面本身的设置都有关系。
在浏览器中,后退到前一个页面 意味着:前一个页面的 html/js/css 等静态资源的请求(甚至是 ajax 动态接口请求)根本不会重新发送,直接使用缓存的响应,而不管这些静态资源响应的缓存策略是否被设置了禁用状态。
各个浏览器测试 demo:先点击按钮使其变色,然后调整至百度,再后退回来查看:
function dispLog(msg) {
var d = new Date();
$("<li />").text(d.toISOString().substr(14, 9) + " " + msg)
.appendTo("#dvDisp");
}
window.addEventListener('load', function(event) {
dispLog("Load Event ");
})
//引入了jq监听ready事件做比较
$(window).ready(function () {
dispLog("Ready Event");
});
$("#btnSetColor").click(function () {
$("#btnSetColor").css("background", "red");
})
window.addEventListener('pageshow', function(event) {
dispLog("PageShow Event " + event.persisted);
//使用event.persisted判断页面是否使用bfcache,值为true或false; 注:如果使用jq绑定事件,需使用event.originalEvent.persisted;
})
window.addEventListener('pagehide', function(event) {
dispLog("Load Event " + event.persisted);
})
| 浏览器 | 首次进入页面 | 点击按钮,再跳转至百度,再后退 |
|---|---|---|
| chrome 58.0.3029.9 | 执行ready→load→pageshow | 重新执行ready→load→pageshow 按钮颜色消失 页面相当于重新刷新了 |
| IE789 | 执行ready→load | 重新执行ready→load 按钮颜色消失 页面相当于重新刷新了 pageshow不兼容IE10- |
| firefox 53.0.2 | 执行ready→load→pageshow | 重新执行ready→load→pageshow 按钮颜色消失 页面相当于重新刷新了 |
| QQBrowser 9.6 | 执行ready→load→pageshow | 重新执行ready→load→pageshow 按钮颜色消失 页面相当于重新刷新了 |
| 360Browser 8.1.1.254 | 执行ready→load→pageshow | 重新执行ready→load→pageshow 按钮颜色消失 页面相当于重新刷新了 |
| Safari 5.1.7(没mac,用的很早版本的windowsafari) | 执行ready→load→pageshow | 新增→pagehide→pageshow 按钮颜色红色 引用了缓存(event.persisted == true) |

| 浏览器 | 首次进入页面 | 点击按钮,再跳转至百度,再后退 |
|---|---|---|
| UCBrowser | 执行ready→load→pageshow | 新增→pagehide→pageshow 按钮颜色红色 引用了缓存(event.persisted == true) |
| QQBrowser | 执行ready→load→pageshow | 新增→pagehide→pageshow 按钮颜色红色 引用了缓存(event.persisted == true) |
| QQ内置浏览器 | 执行ready→load→pageshow | 重新执行ready→load→pageshow 按钮颜色消失 页面相当于重新刷新了 |
| 360Browser | 执行ready→load→pageshow | 无触发事件,按钮颜色红色 引用了缓存(event.persisted == true) |
| chorme | 执行ready→load→pageshow | 重新执行ready→load→pageshow 按钮颜色消失 页面相当于重新刷新了 |
| 微信内置 | 执行ready→load→pageshow | 重新执行ready→load→pageshow 按钮颜色消失 页面相当于重新刷新了 |
| 小米自带 | 执行ready→load→pageshow | 无触发事件,按钮颜色红色 引用了缓存(event.persisted == true) |
| safari | 执行ready→load→pageshow | 新增→pagehide→pageshow 按钮颜色红色 引用了缓存(event.persisted == true) |
效果图:选了三种类型各一个
刷新页面类型

不刷新不执行类型

不刷新执行事件类型(理想型)

这就很头疼了,三种不同的情况,对于 chrome 和 UC 类型的情况,对于我的回退验证码刷新问题,很容易处理,最麻烦的360类型的,但是发现了更蛋疼的情况了: 后退不 reload,不执行 pageshow 的浏览器在公司的一个项目中,居然能实现后退刷新。同一段测试代码,发到项目服务器上,是可以后退刷新, 但是,在我自己的虚拟主机中却不行?是因为请求头部设置吗?
百度、google 类似问题,最多的解决方法:pageshow;
Firefox 和 Opera 有一个新特性,名叫 往返缓存(back-forward cache 或 bfcache),可以在用户使用浏览器的 后退 和 前进 按钮时加快页面的转换速度。
这个缓存中不仅保存着页面数据,还保存了DOM和JavaScript的状态;实际上是将整个页面都保存在了内存里。
如果页面位于 bfcache 中,那么再次打开该页面就不会触发 load 事件。
尽管由于内存中保存了整个页面的状态,不触发 load 事件也不应该会导致什么问题,但为了更形象地说明 bfcache 的行为,Firefox 还是提供了一些新事件。
第一个事件就是 pageshow,这个事件在页面显示时触发,无论页面是否来自 bfcache。在重新加载页面中,pageshow 会在 load 事件触发后触发;
而对于 bfcache 中的页面,pageshow 会在页面状态完全恢复的那一刻触发。另外要注意的是,虽然这个事件的目标是 document,但必须将其事件处理程序添加到window。

