跳转到内容

性能 · 概念

前进后退缓存(bfcache)

发布于

前进后退缓存(bfcache)在用户离开页面时保存整个页面的完整快照,包括 JavaScript 堆和进行中的状态,并在之后的后退或前进导航中即时恢复而不是重新加载。恢复完全跳过网络和脚本执行,比任何 Service Worker 或 HTTP 缓存命中都快,但只有避开了一小串阻碍项的页面才有资格。

离开页面时浏览器把它冻结:定时器和待处理的 Promise 被暂停而不是取消,文档在有限时间内留在内存中。后退或前进回到该条目时页面原地解冻;pageshow 以 persisted === true 触发,load 事件不触发。若页面已被驱逐(内存压力、时间上限或某个阻碍项),该导航就是一次普通加载(Back/forward cache,web.dev)。各引擎都有 bfcache:Chrome 自 96 起在桌面和 Android 上提供,Firefox 和 Safari 则早得多。

  • unload 监听器。 桌面版 Chrome 和 Firefox 拒绝缓存注册了它的页面;Safari 和移动版 Chrome 可能缓存然后跳过触发该事件,这让处理函数在所有引擎里都不可靠。pagehide 在 unload 会触发的所有场合触发,进入缓存时也触发,所以它是替代品。
  • 主文档上的 Cache-Control: no-store。 尽管 bfcache 不是 HTTP 缓存,浏览器一直拒绝缓存这类页面,所以该头只应出现在内容不得以任何形式存储的页面上。
  • 打开的连接。 导航时仍打开的 WebSocket、WebRTC 连接或 IndexedDB 事务会在 Chrome 中阻止缓存;在 pagehide 里关闭它们。
  • 其他阻碍项。 仍然存活的 window.opener 引用、待处理的 beforeunload 提示,以及部分版本中带 body 的进行中 fetch() 请求。Chrome 的清单很长,由下面的 API 按导航逐次报告。

performance.getEntriesByType('navigation')[0].notRestoredReasons(Chrome 125;BCD api.PerformanceNavigationTiming.notRestoredReasons)在恢复后为 null,否则是一个对象,其 reasons 数组列出阻碍项,例如 { reason: "unload-listener" },跨源 iframe 的同一棵树被遮蔽为 null。Firefox 和 Safari 不暴露它;下面的 DevTools 测试工具覆盖开发期的 Chrome,现场 API 覆盖生产环境。

各引擎都有 bfcache:Safari 和 Firefox 已有十余年,Chrome 自 96 起在所有平台提供(Back/forward cache,web.dev)。可观察的差异在于上面的资格规则,以及只有 Chromium 暴露的 notRestoredReasons。

与 Service Worker 和 Core Web Vitals 的关系

Section titled “与 Service Worker 和 Core Web Vitals 的关系”

bfcache 恢复完全不经过 Service Worker 和 HTTP 缓存,而且在 Core Web Vitals 工具中恢复被计为一次独立的页面访问、LCP 接近零,这正是 bfcache 资格能直接改善现场指标的原因。敏感状态(已登出的会话、过期的购物车合计)必须在检查 event.persisted 的 pageshow 处理函数里刷新,因为 load 不会触发。

两个示例都在页面里而不是 Service Worker 里,因为 bfcache 是文档级特性。

pageshow 在首次加载和恢复时都运行;persisted 区分两者。处理函数重新检查会话并重新拉取新鲜度重要的数据,而不重新加载页面。

window.addEventListener('pageshow', (event) => {
if (!event.persisted) return; // 普通加载:load 处理函数已经运行过
refreshSession();
refreshCartTotal();
});
window.addEventListener('pagehide', (event) => {
if (event.persisted) {
socket?.close(); // 页面正进入 bfcache:释放连接
}
});

在 pagehide 中关闭 WebSocket 是页面在 Chrome 中获得资格的关键;在 pageshow 中重新打开则恢复用户期待的实时状态。

在未被恢复的页面上把 notRestoredReasons 发给分析系统,生产环境里真正起作用的阻碍项(例如注册了 unload 的第三方脚本)就能被看见。

function reportBfcacheBlockers() {
const [nav] = performance.getEntriesByType('navigation');
if (!nav || !('notRestoredReasons' in nav)) {
return; // 不支持(Firefox、Safari):改用开发者工具的测试
}
if (nav.type === 'back_forward' && nav.notRestoredReasons) {
navigator.sendBeacon('/analytics/bfcache', JSON.stringify(nav.notRestoredReasons));
}
}

按 nav.type === 'back_forward' 过滤:其他导航类型上该属性虽然存在,但描述的不是页面本可改变的东西。

规范

规范状态
无。