# 前进后退缓存（bfcache）：即时的前进后退导航

> 前进后退缓存如何在内存中保存整页快照——包括 JavaScript 堆——从而让前进后退导航能够即时恢复页面；为何 unload 监听器在部分浏览器中可能阻止其可用性；以及如何用 pageshow 事件的 persisted 属性检测一次 bfcache 恢复。

**一句话：** 根据 MDN，前进后退缓存（bfcache）是一项性能特性，它在用户离开页面时保存
该页面的完整快照——包括 JavaScript 堆——从而让浏览器在之后的前进或后退导航中即时恢复
它，而不必重复加载该页面所需的网络请求。

## 为何比 HTTP 缓存更快

根据 MDN，普通的 HTTP 缓存条目只保存之前请求的响应；而 bfcache 保存的是内存中完整的
活动页面，用户离开时正在进行的代码会被暂停，返回时再恢复执行。根据 web.dev，这使得
bfcache 恢复比即便经过良好优化、依赖 HTTP 缓存的加载还要更快。

## 特性检测与回退

下面是包裹在存在性检查中的 `pageshow`，通过 `event.persisted` 区分 bfcache
恢复与正常加载：

```js
if ('onpageshow' in window) {
  window.addEventListener('pageshow', (event) => {
    if (event.persisted) {
      console.log('此页面是从 bfcache 恢复的。');
    } else {
      console.log('此页面是正常加载的。');
    }
  });
} else {
  // 针对不支持 pageshow 的浏览器的回退处理。
  console.log('此页面是正常加载的。');
}
```

根据 web.dev，`pageshow` 会在首次加载页面时紧随 `load` 事件触发，并且在页面从
bfcache 恢复时再次触发；它的 `persisted` 属性用于区分这两种情况。

## 支持情况

根据 web.dev，所有主流浏览器都包含 bfcache，其中 Chrome 自版本 96 起支持，Firefox
和 Safari 同样支持。

## 常见问题

- **`unload` 监听器可能阻止 bfcache 可用性。** 根据 MDN，某些代码特性——MDN 举出的
  例子是 `unload` 事件处理程序——与 bfcache 不兼容，其存在可能阻止页面被缓存。根据
  web.dev，具体而言，这会使页面在桌面版 Chrome 和 Firefox 中不符合缓存条件，而
  Safari 仍可能缓存这类页面，移动版 Chrome 和 Safari 也可能尝试缓存。根据 web.dev，
  推荐的替代方案是 `pagehide`，它会在 `unload` 原本会触发的每种情况下触发，并且还会
  在页面进入 bfcache 时触发。
- **待处理任务会被暂停，而非取消。** 根据 MDN，页面被存入缓存时，正在进行的
  JavaScript 会被暂停，恢复时再继续执行——代码不应假设定时器或正在进行的 promise 会
  在页面不可见期间持续运行。
- **bfcache 恢复不是一次全新的页面加载。** 由于 `pageshow` 可能在没有前置 `load`
  事件的情况下触发，只在 `load` 监听器中刷新过期或敏感 UI 状态的代码会错过一次恢复；
  应检查 `event.persisted` 并在那里同样更新或重新获取该状态。
- **用监测阻塞原因取代猜测。** 根据 MDN，
  `PerformanceNavigationTiming.notRestoredReasons` API 会报告某次特定导航为何未从
  bfcache 恢复，这比单凭行为推断原因更可靠。

## 相关参考

- [核心网页指标](/zh/reference/performance/core-web-vitals/) —— bfcache 恢复在核心
  网页指标报告工具中会被计为独立的一次页面访问。
- [推测规则 API](/zh/reference/performance/speculation-rules/) —— 本站的另一篇
  性能参考文档。