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.
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 byevent.persisted
.
1 | window.addEventListener('pagehide', (e) => { |
Enable conditions and restrictions
While all modern browsers support BFCache, not all pages can be cached. The following are common blocking factors:
Conditions | Block caching | Description |
---|---|---|
Use unload event | ✅ | Recommend using pagehide instead |
The page uses WebSocket | ✅ | The connection must be closed manually |
There is beforeunload processing logic | ✅ | Will be explicitly blocked |
The page contains document.write | ✅ | Incompatible |
There is a cross-domain iframe | It is possible | It 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 towindow
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 | window.addEventListener('click', (e) => { |
After restoring the page, two logs failed to print normally.
1 | window.addEventListener('click', (e) => { |
- ❌ But starting from
var element = document.getElementById('my-element')
in try, the entire logic is “disconnected”, and evenconsole.log(element)
is not reached; - ❌ There is no error output, which means that even the catch is not triggered.
Cause Analysis
The event system may not be reinitialized to
window
after recovery
In some browser implementations, the page restored from BFCache will not triggerwindow.onload
again, and some event bindings may be lost due to optimization.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 onwindow
may not take effect.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 towindow
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:
Recommended Solutions
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 | document.addEventListener('click', handler); |
Rebind key events in pageshow
1 | function bindClick() { |
Avoid using unload
, use pagehide
instead
1 | window.addEventListener('pagehide', (e) => { |
Determine whether the page comes from BFCache
Observe the event.persisted
field of the event callback in the console
1 | window.addEventListener('pageshow', e => { |
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.