监控 bfcache 阻止原因

可用性有限

此特性不是基线特性,因为它在一些最广泛使用的浏览器中不起作用。

实验性: 这是一项实验性技术
在生产中使用此技术之前,请仔细检查浏览器兼容性表格

PerformanceNavigationTiming.notRestoredReasons 属性报告了当前文档在导航时被阻止使用 bfcache 的信息。开发者可以使用这些信息来识别需要更新才能与 bfcache 兼容的页面,从而提高网站性能。

前进/后退缓存(bfcache)

现代浏览器为历史导航提供了一种优化功能,称为前进/后退缓存(bfcache)。这使用户在返回已访问过的页面时能获得即时加载体验。页面可能因各种原因被阻止进入 bfcache,或在 bfcache 中被驱逐,其中一些是规范要求的,另一些是浏览器实现特有的。

为了能够监控 bfcache 阻塞原因,PerformanceNavigationTiming 类包含一个 notRestoredReasons 属性。该属性返回一个 NotRestoredReasons 对象,其中包含有关顶级帧和文档中所有 <iframe> 的相关信息。

  • bfcache 使用被阻止的原因。
  • 详细信息,例如帧的 idname,以帮助识别 HTML 中的 <iframe>

注意: 历史上,已废弃的 PerformanceNavigation.type 属性用于监控 bfcache,开发者通过测试 type"TYPE_BACK_FORWARD" 来获取 bfcache 命中率的指示。然而,这并未提供任何 bfcache 阻塞的原因或其他数据。今后应使用 notRestoredReasons 属性来监控 bfcache 阻塞。

记录 bfcache 阻塞原因

可以使用 PerformanceObserver 获取持续的 bfcache 阻塞数据,如下所示:

js
const observer = new PerformanceObserver((list) => {
  let perfEntries = list.getEntries();
  perfEntries.forEach((navEntry) => {
    console.log(navEntry.notRestoredReasons);
  });
});

observer.observe({ type: "navigation", buffered: true });

或者,您可以使用合适的方法(例如 Performance.getEntriesByType())获取历史 bfcache 阻塞数据。

js
function returnNRR() {
  const navEntries = performance.getEntriesByType("navigation");
  for (let i = 0; i < navEntries.length; i++) {
    console.log(`Navigation entry ${i}`);
    let navEntry = navEntries[i];
    console.log(navEntry.notRestoredReasons);
  }
}

上面显示的代码片段会将 NotRestoredReasons 对象记录到控制台。这些对象具有以下结构,代表了顶级帧的阻塞状态:

json
{
  "children": [],
  "id": null,
  "name": null,
  "reasons": [{ "reason": "unload-listener" }],
  "src": "",
  "url": "example.com"
}

属性如下:

children 只读 实验性

一个 NotRestoredReasons 对象数组,每个对象对应当前文档中嵌入的子 <iframe>,其中可能包含与子帧相关的顶级帧被阻止的原因。每个对象都具有与父对象相同的结构——这样,对象中可以递归表示任意数量的嵌入 <iframe> 级别。如果帧没有子级,数组将为空;如果文档位于跨域 <iframe> 中,children 将返回 null

id 只读 实验性

一个字符串,表示文档所包含的 <iframe>id 属性值(例如 <iframe id="foo" src="...">)。如果文档不在 <iframe> 中,或者 <iframe> 没有设置 id,则 id 将返回 null

name 只读 实验性

一个字符串,表示文档所包含的 <iframe>name 属性值(例如 <iframe name="bar" src="...">)。如果文档不在 <iframe> 中,或者 <iframe> 没有设置 name,则 name 将返回 null

reasons 只读 实验性

一个 NotRestoredReasonDetails 对象数组,每个对象表示导航页面被阻止使用 bfcache 的一个原因。如果文档位于跨域 <iframe> 中,reasons 将返回 null,但如果任何 <iframe> 阻止了顶级帧的 bfcache 使用,则父文档可能会显示 "masked"reason。有关原因的更多详细信息,请参阅阻塞原因

src 只读 实验性

一个字符串,表示文档所包含的 <iframe> 的源路径(例如 <iframe src="exampleframe.html">)。如果文档不在 <iframe> 中,则 src 将返回 null

url 只读 实验性

一个字符串,表示导航页面或 <iframe> 的 URL。如果文档位于跨域 <iframe> 中,url 将返回 null

报告同源 <iframe> 中的 bfcache 阻塞

当页面嵌入同源 <iframe> 时,返回的 notRestoredReasons 值将包含一个对象数组,位于 children 属性中,表示与每个嵌入帧相关的阻塞原因。

例如

json
{
  "children": [
    {
      "children": [],
      "id": "iframe-id",
      "name": "iframe-name",
      "reasons": [],
      "src": "./index.html",
      "url": "https://www.example.com/iframe-examples.html"
    },
    {
      "children": [],
      "id": "iframe-id2",
      "name": "iframe-name2",
      "reasons": [{ "reason": "unload-listener" }],
      "src": "./unload-examples.html",
      "url": "https://www.example.com/unload-examples.html"
    }
  ],
  "id": null,
  "name": null,
  "reasons": [],
  "src": null,
  "url": "https://www.example.com"
}

报告跨域 <iframe> 中的 bfcache 阻塞

当页面嵌入跨域帧时,为了避免泄露跨域信息,共享的有关它们的信息量受到限制。只包含外部页面已知的信息,以及跨域子树是否导致 bfcache 阻塞。不包含阻塞原因或有关子树较低级别的信息(即使某些子级别是同源的)。

例如

json
{
  "children": [
    {
      "children": [],
      "id": "iframe-id",
      "name": "iframe-name",
      "reasons": [],
      "src": "https://www.example2.com/",
      "url": null
    }
  ],
  "id": null,
  "name": null,
  "reasons": [{ "reason": "masked" }],
  "src": null,
  "url": "https://www.example.com"
}

对于所有跨域 <iframe>,不报告任何阻塞原因;对于顶级帧,报告 "masked" 原因,以表示出于隐私目的隐藏了原因。请注意,"masked" 也可能用于隐藏用户代理特定的原因;它并不总是表示 <iframe> 中存在问题。

阻塞原因

可能导致阻塞的原因有很多。尽管原因已标准化,但开发者应避免依赖于特定的原因措辞,并准备好处理新添加和删除的原因。

规范中列出的值是:

"fetch"

在卸载过程中,当前文档发起的一个 fetch(例如,通过 fetch())在进行中被取消。因此,页面处于不稳定状态,无法存储在 bfcache 中。

"lock"

在卸载过程中,持有的锁和锁请求被终止,因此页面处于不稳定状态,无法存储在 bfcache 中。

"masked"

确切原因出于隐私目的被隐藏。此值可能表示以下其中一项:

  • 当前文档包含在跨域 <iframe> 中的子级,它们阻止了在 bfcache 中的存储。
  • 由于用户代理特定的原因,当前文档无法存储在 bfcache 中。

创建当前文档的原始导航出错,阻止了将生成的错误文档存储在 bfcache 中。

"parser-aborted"

当前文档从未完成其初始 HTML 解析,阻止了将未完成的文档存储在 bfcache 中。

"websocket"

在卸载过程中,一个打开的 WebSocket 连接被关闭,因此页面处于不稳定状态,无法存储在 bfcache 中。

用户代理特定的阻塞原因

还指定了一些浏览器可能使用的其他阻塞原因:

"audio-capture"

文档通过使用 Media Capture and Streams 的 getUserMedia() 并带有音频,请求了音频捕获权限。

"background-work"

文档通过调用 SyncManagerregister() 方法、PeriodicSyncManagerregister() 方法或 BackgroundFetchManagerfetch() 方法请求了后台工作。

"broadcastchannel-message"

当页面存储在前进/后退缓存中时,页面上的 BroadcastChannel 连接接收到一条消息以触发 message 事件。

"idbversionchangeevent"

在卸载过程中,文档有一个待处理的 IDBVersionChangeEvent

"idledetector"

在卸载过程中,文档有一个活动的 IdleDetector

"keyboardlock"

在卸载过程中,由于调用了 Keyboardlock() 方法,键盘锁定仍然处于活动状态。

"mediastream"

在卸载时,一个 MediaStreamTrack 处于活动状态。

"midi"

文档通过调用 navigator.requestMIDIAccess() 请求了 MIDI 权限。

"modals"

在卸载过程中显示了用户提示。

在卸载过程中,加载仍在进行中,因此文档未处于可以存储在前进/后退缓存中的状态。

导航请求通过调用 window.stop() 被取消,页面未处于可以存储在前进/后退缓存中的状态。

"non-trivial-browsing-context-group"

此文档的浏览上下文组有多个顶级浏览上下文。

"otpcredential"

文档创建了一个 OTPCredential

"outstanding-network-request"

在卸载过程中,文档有未完成的网络请求,并且未处于可以存储在前进/后退缓存中的状态。

"paymentrequest"

在卸载过程中,文档有一个活动的 PaymentRequest

"pictureinpicturewindow"

在卸载过程中,文档有一个活动的 PictureInPictureWindow

"plugins"

文档包含插件。

"request-method-not-get"

文档是通过 HTTP 请求创建的,该请求的方法不是 GET

"response-auth-required"

文档是通过需要 HTTP 认证的 HTTP 响应创建的。

"response-cache-control-no-store"

文档是通过 HTTP 响应创建的,该响应的 Cache-Control 头部包含“no-store”令牌。

"response-cache-control-no-cache"

文档是通过 HTTP 响应创建的,该响应的 Cache-Control 头部包含“no-cache”令牌。

"response-keep-alive"

文档是通过包含 Keep-Alive 头部的 HTTP 响应创建的。

"response-scheme-not-http-or-https"

文档是通过其 URL 方案不是 HTTP(S) 方案的响应创建的。

"response-status-not-ok"

文档是通过其状态不是 ok 状态的 HTTP 响应创建的。

"rtc"

在卸载过程中,一个 RTCPeerConnectionRTCDataChannel 被关闭,因此页面未处于可以存储在前进/后退缓存中的状态。

"sensors"

文档请求了传感器访问权限。

"serviceworker-added"

当页面位于前进/后退缓存中时,文档的服务工作客户端开始被 服务工作器 控制。

"serviceworker-claimed"

当页面位于前进/后退缓存中时,文档的服务工作客户端的活动 服务工作器 被声明。

"serviceworker-postmessage"

当页面位于前进/后退缓存中时,文档的服务工作客户端的活动 服务工作器 收到了一条消息。

"serviceworker-version-activated"

当页面位于前进/后退缓存中时,文档的服务工作客户端的活动 服务工作器 的版本被激活。

"serviceworker-unregistered"

当页面位于前进/后退缓存中时,文档的服务工作客户端的活动 服务工作器 的服务工作器注册被注销。

"sharedworker"

此文档位于 SharedWorkerGlobalScope 的所有者集中。

"smartcardconnection"

在卸载过程中,文档有一个活动的 SmartCardConnection

"speechrecognition"

在卸载过程中,文档有一个活动的 SpeechRecognition

"storageaccess"

文档通过使用 Storage Access API 请求了存储访问权限。

"unload-listener"

文档为 unload 事件注册了一个事件监听器。

"video-capture"

文档通过使用 Media Capture and Streams 的 getUserMedia() 并带有视频,请求了视频捕获权限。

"webhid"

文档调用了 WebHID APIrequestDevice() 方法。

"webshare"

文档使用了 Web Share APInavigator.share() 方法。

"webtransport"

在卸载过程中,一个打开的 WebTransport 连接被关闭,因此页面未处于可以存储在前进/后退缓存中的状态。

"webxrdevice"

文档创建了一个 XRSystem

浏览器兼容性

另见

注意: 本文改编自 Chris Mills 和 Barry Pollard 的 前进/后退缓存 notRestoredReasons API,最初于 2023 年在 developer.chrome.com 上发布,采用 知识共享署名 4.0 许可