什么是 bfcache?如何让页面更容易命中浏览器前进后退缓存?
简化版
bfcache 是浏览器在前进/后退导航时保存页面完整快照的缓存,命中后可几乎瞬间恢复页面状态。优化关键是避免阻止缓存的行为,比如不必要的 unload 监听、敏感页面错误缓存策略、未关闭的连接或页面生命周期处理不当。
详细版
bfcache 不同于 HTTP 缓存。HTTP 缓存缓存资源文件,bfcache 缓存整个页面状态,包括 DOM、JS 堆和滚动位置。用户从列表进详情再返回列表时,bfcache 命中能显著改善体验。
页面要监听 pageshow 和 pagehide,通过 event.persisted 判断是否从 bfcache 恢复或进入缓存。离开页面时应暂停计时器、视频、连接和敏感任务,恢复时重新校验必要数据。
面试回答要强调:不要用 unload 做清理,优先使用 pagehide;同时用 Chrome DevTools bfcache 测试工具检查阻塞原因。
完整版教学
一、bfcache 优化的是返回体验
很多网站只优化首次进入,却忽略“列表页 -> 详情页 -> 返回列表页”这个高频路径。bfcache 命中时,浏览器可以恢复之前页面快照,而不是重新请求 HTML、重新执行 JS、重新渲染列表。
普通返回:请求 HTML -> 下载资源 -> 执行 JS -> 恢复列表
bfcache:恢复快照 -> 几乎立即可交互
记忆钩子:HTTP 缓存存资源,bfcache 存页面快照。
二、bfcache 和 HTTP 缓存不是一回事
HTTP 缓存关注单个资源响应,比如 JS、CSS、图片或接口;bfcache 关注整个页面的内存状态。即使 JS 文件都命中 HTTP 缓存,页面仍要重新执行脚本和恢复状态。
| 维度 | HTTP 缓存 | bfcache |
|---|---|---|
| 缓存对象 | 资源响应 | 完整页面快照 |
| 典型场景 | 静态资源复用 | 前进后退导航 |
| 恢复状态 | 需要应用自己恢复 | DOM/滚动/JS 状态可恢复 |
| 失效原因 | 缓存头、版本 | 页面生命周期和安全限制 |
如果列表页重建需要 1.5 秒,bfcache 命中可能让返回体感接近 0.1 秒。对电商、内容流、搜索结果页,这个收益很真实。
三、页面生命周期事件要用对
unload 事件不可靠,也常使页面不适合进入 bfcache。更推荐使用 pagehide 和 pageshow,并根据 persisted 判断缓存状态。
window.addEventListener('pagehide', (event) => {
pauseRealtimeWork()
})
window.addEventListener('pageshow', (event) => {
if (event.persisted) {
refreshIfNeeded()
}
})
pagehide 表示页面正在被隐藏或离开,可能进入 bfcache;pageshow 在页面首次显示和从 bfcache 恢复时都会触发。恢复后可以轻量校验数据,而不是无脑整页刷新。
四、哪些行为会降低 bfcache 命中
浏览器会避免缓存可能不安全或无法正确恢复的页面。不同浏览器规则会演进,但一些高风险行为长期值得注意。
| 行为 | 问题 | 建议 |
|---|---|---|
| unload 监听 | 阻碍缓存且不可靠 | 改用 pagehide/visibilitychange |
| 未关闭连接 | 状态难冻结 | pagehide 暂停,pageshow 恢复 |
| 敏感页面 | 返回可能暴露数据 | 明确缓存策略和重新校验 |
| 打开的锁或设备 | 状态不可恢复 | 离开时释放 |
例如聊天页保持 WebSocket 并不一定永远不能缓存,但需要在页面隐藏时处理连接和状态。工程上要看 DevTools 给出的具体阻塞原因。
五、从 bfcache 恢复后要处理数据新鲜度
bfcache 恢复的是旧页面快照。对静态内容这是优点;对订单状态、余额、权限等页面,旧数据可能不合适。正确做法不是禁用 bfcache,而是恢复后做必要校验。
window.addEventListener('pageshow', (event) => {
if (event.persisted && Date.now() - lastFetchAt > 30000) {
refetchSummary()
}
})
比如 30 秒内返回列表直接恢复,超过 30 秒再轻量刷新数据。这样既保留快速返回体验,又避免长时间旧数据误导用户。
六、如何测试和监控 bfcache
Chrome DevTools 的 Application 面板提供 Back/forward cache 测试,可以列出页面是否可缓存以及阻塞原因。真实用户侧也可以通过 pageshow.persisted 统计恢复比例。
window.addEventListener('pageshow', (event) => {
report('pageshow', { bfcacheRestore: event.persisted })
})
假设详情返回列表每天 10 万次,bfcache 命中率从 20% 提到 70%,就有 5 万次返回体验从重建变成快照恢复。这个指标适合和页面跳出、转化路径一起观察。
七、常见误区与追问
- 误区:bfcache 就是 HTTP 强缓存。 它保存完整页面快照,不只是资源文件。
- 误区:返回时必须重新加载才能保证数据新鲜。 可以恢复后按条件轻量校验,不必牺牲所有返回体验。
- 误区:unload 是最适合做离开清理的事件。 它不可靠且可能阻碍 bfcache,应优先使用 pagehide。
- 追问:如何判断页面从 bfcache 恢复? 在
pageshow事件里检查event.persisted。 - 追问:哪些页面不适合缓存? 高敏感信息、状态难恢复或安全要求明确禁止恢复的页面要谨慎。
- 追问:如何提升命中率? 移除 unload、正确处理连接和生命周期、用 DevTools 修复阻塞原因。
八、加强记忆
bfcache 按“快照、生命周期、新鲜度、命中率”记:它让前进后退恢复完整页面状态,不等同于 HTTP 缓存;页面用 pagehide/pageshow 管暂停和恢复,用条件刷新处理数据新鲜度,再通过 DevTools 和 RUM 统计命中率。