愚木混株 Yumu

Back Forward Cache

What’s BFCache

With back/forward cache (BFCache), instead of destroying a page when the user navigates away, we postpone destruction and pause JS execution. If the user navigates back soon, we make the page visible again and unpause JS execution. This results in a near instant page navigation for the user.

How many times have you visited a website and clicked a link to go to another page, only to realize it’s not what you wanted and click the back button? In that moment, BFCache can make a big difference in how fast the previous page loads.

https://web.dev/articles/bfcache

Back Forward Cache (BFCache) is a session-level cache mechanism implemented by modern browsers. It saves the page completely (including DOM tree, JavaScript memory state, scroll position, etc.) when the user leaves the page, and restores the page state instantly when the user clicks the “Back” or “Forward” button, without reloading page resources.

Unlike traditional page refreshes, BFCache does not trigger load/unload events, but introduces a new event mechanism.

Lifecycle Events

  • pagehide: triggered when the page is hidden (navigated away, entered BFCache), event.persisted marks whether it has entered BFCache.
  • pageshow: triggered when the page is displayed (including first loading and restoration from BFCache), which can also be judged by event.persisted.
1
2
3
4
5
6
7
window.addEventListener('pagehide', (e) => {
console.log('pagehide', e.persisted);
});

window.addEventListener('pageshow', (e) => {
console.log('pageshow', e.persisted);
});

Enable conditions and restrictions

While all modern browsers support BFCache, not all pages can be cached. The following are common blocking factors:

ConditionsBlock cachingDescription
Use unload eventRecommend using pagehide instead
The page uses WebSocketThe connection must be closed manually
There is beforeunload processing logicWill be explicitly blocked
The page contains document.writeIncompatible
There is a cross-domain iframeIt is possibleIt will affect the cache eligibility of the main page

Bug Analysis: The click listener on the window is invalid

In some debugging scenarios, you may encounter a bug like this:

The click event bound to window works fine when the page first loads, but once the user navigates back to the page using the browser’s “back” function, the event no longer fires, even though the page looks exactly the same as before they left.

This phenomenon usually occurs when using BFCache to restore the page, and is caused by improper binding timing or object selection. Different browser kernels may also handle BFCache in a way that does not match the expected behavior.

Example of how to reproduce the problem

Add a click event listener to window, which will capture all click events that bubble up. Under normal circumstances, when a click is triggered, can see these logs:

  • consol.log(e)
  • console.log(element)
1
2
3
4
5
6
7
8
9
window.addEventListener('click', (e) => {
consol.log(e); // log printed
try {
var element = document.getElementById('my-element');
console.log(element); // log printed
} catch (error) {
console.log(error);
}
});

After restoring the page, two logs failed to print normally.

1
2
3
4
5
6
7
8
9
window.addEventListener('click', (e) => {
consol.log(e); // log printed
try {
var element = document.getElementById('my-element');
console.log(element); // log not printed
} catch (error) {
console.log(error);
}
});
  • ❌ But starting from var element = document.getElementById('my-element') in try, the entire logic is “disconnected”, and even console.log(element) is not reached;
  • ❌ There is no error output, which means that even the catch is not triggered.

Cause Analysis

  1. The event system may not be reinitialized to window after recovery
    In some browser implementations, the page restored from BFCache will not trigger window.onload again, and some event bindings may be lost due to optimization.

  2. The DOM and JS heap memory states are preserved, but the listeners may have been cleaned up or frozen
    Especially when it comes to security mechanisms (such as cross-document navigation isolation), the listeners on window may not take effect.

  3. Behavior interference of SPA frameworks
    Some frameworks such as React/Vue/Next.js will remount or diff elements when the page is restored, causing the events bound to window to miss the binding time.

Reason in this case: The page has been restored but the global event system has not been restored

In some browsers, after BFCache is restored, the event listener on the window becomes invalid, but it will not automatically report an error or be removed. In other words:
• window.addEventListener(‘click’, …) is still in memory;
• But after restoration, this listener is no longer triggered, as if the binding is lost.

Solution:

To ensure that events can be reliably triggered in BFCache scenarios, the following strategies are recommended:

Try to bind to specific elements instead of window

1
2
3
document.addEventListener('click', handler);
// or
document.body.addEventListener('click', handler);

Rebind key events in pageshow

1
2
3
4
5
6
7
8
9
10
11
12
function bindClick() {
document.body.addEventListener('click', handler);
}

window.addEventListener('load', bindClick);

window.addEventListener('pageshow', (e) => {
if (e.persisted) {
// Rebind when restoring from BFCache
bindClick();
}
});

Avoid using unload, use pagehide instead

1
2
3
4
5
window.addEventListener('pagehide', (e) => {
if (!e.persisted) {
// The cleanup logic is executed only when you actually leave the page
}
});

Determine whether the page comes from BFCache

Observe the event.persisted field of the event callback in the console

1
2
3
4
5
window.addEventListener('pageshow', e => {
if (e.persisted) {
console.log('The page is restored from the BFCache');
}
});

Conclusion

As browsers continue to optimize performance, BFCache is gradually becoming an important factor affecting page state management and event behavior. Understanding its working principles and limitations is a necessary skill for building modern Web applications.

Especially when debugging problems such as “event listener failure” that are difficult to reproduce on the surface, considering whether it is related to BFCache is one of the key breakthrough points in the direction of troubleshooting.