onload仅在首次加载或硬刷新时触发,bfcache恢复时不执行;pageshow配合event.persisted为true可精准捕获该场景并手动刷新。

onload 不会在 bfcache 恢复时触发
这是最直接的区别:onload 只在页面**首次加载或硬刷新**时执行,一旦页面被存入往返缓存(bfcache),用户点后退/前进按钮返回该页,onload 就完全静默——DOM、JS 状态照旧,但所有依赖 onload 的初始化逻辑(比如拉新数据、重置表单、更新时间戳)全部跳过。
常见错误现象:页面回退后显示过期的订单状态、验证码没刷新、倒计时继续从旧值走、地图 marker 重复叠加。这些都不是代码写错了,而是 onload 根本没跑。
-
onload触发前提是资源重新加载,bfcache 下无加载行为 - 即使你把
onload写在<body onload="...">或window.onload = ...,结果一样 - 移动端 Safari 和 Chrome for Android 对 bfcache 更激进,问题更明显
onpageshow 是唯一能捕获 bfcache 恢复的事件
onpageshow 不是 <body> 的合法内联属性,**不能写成 <body onpageshow="...">** —— 这种写法在现代浏览器中基本不生效,且无法访问 event.persisted。唯一可靠方式是绑定到 window:
window.addEventListener('pageshow', function(e) {
if (e.persisted) {
// 页面从 bfcache 恢复:需手动刷新时间、重 fetch、重置表单
document.getElementById('timestamp').textContent = new Date().toISOString();
loadData();
} else {
// 首次加载或硬刷新:可执行完整初始化
}
});关键点在于 e.persisted:它为 true 表示页面来自 bfcache,false 表示全新加载。这个布尔值是判断依据,没有它,pageshow 和 load 就失去了区分意义。
立即学习“前端免费学习笔记(深入)”;
-
pageshow总是触发,无论来源;load只在非缓存路径触发 -
pageshow在 DOM 完全恢复后立即触发,比DOMContentLoaded略晚,但早于大多数用户交互 - 不要试图用
setTimeout延迟处理 —— bfcache 恢复后的 JS 执行环境是冻结快照,延迟可能错过状态同步时机
为什么不能靠 meta 标签禁用 bfcache
加 <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"> 这类标签,对 bfcache **基本无效**。bfcache 是内存级机制,和 HTTP 缓存无关,它不读取响应头或 meta 指令,只由浏览器策略和页面行为(如监听 pagehide、使用 beforeunload、打开弹窗等)决定是否启用。
真正能阻止进入 bfcache 的方式极少且副作用大:
- 监听
pagehide并调用event.preventDefault()(但规范已废弃,Chrome 86+ 忽略) - 在页面中调用
window.open()、alert()、confirm()(会破坏用户体验) - 使用
beforeunload事件(但会禁用 bfcache,且现代浏览器限制其使用场景)
所以,与其对抗 bfcache,不如接受它,并用 pageshow + e.persisted 做状态修复 —— 这是当前最稳定、兼容性最好的路径。
pageshow 中容易忽略的 DOM 状态陷阱
bfcache 恢复时,DOM 节点是“活”的,但它们的状态可能已失效:input 的 value 属性还是旧值,checked 状态没变,textarea 光标位置错乱,甚至 addEventListener 绑定的回调还在,但闭包里的变量早已过期。
这意味着仅靠重绘 UI 不够,必须主动同步:
- 表单控件:手动赋值
input.value = '',而非只清空innerHTML - 定时器:检查并清除旧
setInterval/setTimeoutID,避免多个副本并发运行 - 第三方库:如 Chart.js 实例需调用
.destroy()再重建,否则 canvas 渲染异常 - 滚动位置:
scrollTo(0, 0)不一定生效,需结合requestAnimationFrame或检测document.scrollingElement.scrollTop
最麻烦的不是代码量,而是“哪些状态需要重置”往往要靠真实回退操作反复验证 —— 开发者本地测试常漏掉这一环。



















