BFCache-related
Preface
In some browsers, the return button is directly cached and does not execute any js code. For example, if the button is set to be loaded when submitting, and if the button is not processed after the successful submission, the button will still be loaded after returning, which is a very bad experience.
This is because: Some browsers don’t trigger the onload event when backing up, which is one of the new features of the HTML5 generation of browsers - Back-Forward Cache (bfcache for short)
What is bfcache
The familiar red book, Advanced Programming in JavaScript, has a reference to bfcache:
bfcache, or back-forward cache, can be referred to as “round-trip caching”, which speeds up page transitions when users use the browser’s “back” and “forward” buttons. buttons of the browser. This cache holds not only the page data, but also the state of the DOM and JS, in effect storing the entire page in memory. If the page is in the bfcache, opening it again will not trigger the onload event.
pageshow event
This event is triggered when the page is displayed, regardless of whether the page is from bfcache or not. pageshow will be triggered after the load event is triggered for reloaded pages, and for pages in bfcache, it will be triggered the moment the page state is fully restored.
The pagehide event
This event is triggered when the browser unloads the page, and it is triggered before the unload event.
The persisted attribute
The event objects for the pageshow event and the pagehide event also contain a boolean-valued property called persisted.
- For the pageshow event, the value of this attribute is true if the page was loaded from the bfcache; otherwise, the value of this attribute is false.
- For pagehide events, the value of this attribute is true if the page was saved in the bfcache after unloading; otherwise, the value of this attribute is false.
Different browsers are not uniform in how they show that the browser “opens” the previous page in the history of the current window, which is related to the browser’s implementation as well as the settings of the page itself.
Testing
Test code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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);
})
DOM
1
2
3
4
5
<body>
<a href="https://www.sina.com.cn/">前往大新浪</a>
<input type="button" id="changeColor" value="变红" />
<ul></ul>
</body>
Test results
| browser | result | | ————-| —–| | IE 11 | New load triggers load and pageshow events, red is not preserved, no bfcache | | Chrome 52.0.2743.116 | New load triggers load and pageshow events, red is not reserved, no bfcache | | Opera 39.0 | New load triggers load and pageshow events, red is unreserved, no bfcache | Firefox 48.0.2743.116 | New load triggers load and pageshow events, red is unreserved, no bfcache | Firefox 48.0.2 | New load triggers load and pageshow events, pagehide event triggered when clicking to go to Big New Wave, but does not deposit bfcache, red color is not preserved | | Safari (iPhone) iOS 9.3.5 | New load triggers load and pageshow events, tap to go to Big New Wave triggers pagehide event, but does not store in bfcache, red color is retained | | UC | New load triggers load and pageshow events, tap to go to Big New Wave triggers pagehide event, save to bfcache, red reserved | | qqbrowser | [ios 9.3.5] New load triggers load and pageshow events, no event is triggered when going back to previous page and the red color is retained, there is a bfcache [Android] New load triggers load and pageshow events, pagehide event is triggered when tapping to Big New Wave, it shows that there is no bfcache deposited but the red color is retained, there is a bfcache but the red color is retained. Reserved, has bfcache but does not support the persisted attribute of the pagehide event (results vary by model) | browser | new load triggers load and pageshow events. | browser | new load triggers load and pageshow events, no event triggered when going back to previous page and red color is preserved, bfcache is present (doubtful) As you can see, Safari, Firefox, UC, qq browser, browser retains the red color and has round-trip cache. On returning to the previous page, the
- Safari, UC and qq browsers do not trigger load;
- IE, Firefox, Chrome and Opera trigger load;
- browser does not trigger any event.
Solution
Firefox’s developer documentation provides some ideas:
- The page is listening for unload or beforeunload events; * The page has “cache-control: no-store”.
- The page has “cache-control: no-store” set.
- The site uses HTTPS and the page meets at least one of the following conditions.
- “Cache-Control: no-cache”.
- “Pragma: no-cache”.
- Set request header “Expires: 0” or “Expires” to a value before “Date” (unless “ Cache-Control: max-age=” is also set).
- The page is not fully loaded when the user moves forward and backward or it has an ongoing network request, such as an XMLHttpRequest; * The page is in the middle of an Indexed Request.
- The page is performing an IndexedDB operation.
- The top-level page contains frames, and those frames cannot be cached for any of the reasons listed here.
- The page is in a frame, and the user jumps to a new page within that frame, the newly loaded page will be cached here.
JS listens for pageshow event to prevent pages from going to bfcache
Release the code at the drop of a hat.
1
2
3
4
5
window.addEventListener('pageshow', function( e ){
if (e.persisted) {
window.location.reload()
}
})
Safari, UC, qq browser test pass. But UC, qq browser will flash the page in bfcache first, because pageshow is triggered after the load event is triggered. browser will still keep the red color, I think it’s because browser doesn’t trigger any event when it goes back to the previous page.
JS listens for pagehide event to prevent page from entering bfcache
Release the code at the drop of a hat.
1
2
3
4
5
6
7
8
9
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 test passed. browser will still keep the red color, I think it’s because browser doesn’t trigger any event when it goes back to the previous page.
Add a Cache-Control header to the response
The code example is as follows: Add the following disable cache setting to the header section of the jsp template:
1
2
3
4
5
6
7
<head>
<%
response.setHeader("Cache-Control","no-cache,no-store,must-revalidate");
response.setHeader("Expires", "0");
response.setHeader("Pragma","no-cache");
%>
</head>
Solution for android webview cache
The premise of this solution is that the browser uses jsp to regenerate the html each time it requests a page from the server. it requires that the page itself has cache disabled.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- 安卓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 -->
Summary
- PC browser, set disable page cache header can realize the backward refreshing
- mobile browsers that support bfcache/page cache, JS listens to pageshow/pagehide, and force refresh when backward is detected.
- in the case of the first two options do not work, you can write in the HTML server-side page generation version number, and the version number of the local storage to determine whether the backward and use the cached page