监控 bfcache 阻止原因
PerformanceNavigationTiming.notRestoredReasons 属性报告了当前文档在导航时被阻止使用 bfcache 的信息。开发者可以使用这些信息来识别需要更新才能与 bfcache 兼容的页面,从而提高网站性能。
前进/后退缓存(bfcache)
现代浏览器为历史导航提供了一种优化功能,称为前进/后退缓存(bfcache)。这使用户在返回已访问过的页面时能获得即时加载体验。页面可能因各种原因被阻止进入 bfcache,或在 bfcache 中被驱逐,其中一些是规范要求的,另一些是浏览器实现特有的。
为了能够监控 bfcache 阻塞原因,PerformanceNavigationTiming 类包含一个 notRestoredReasons 属性。该属性返回一个 NotRestoredReasons 对象,其中包含有关顶级帧和文档中所有 <iframe> 的相关信息。
- bfcache 使用被阻止的原因。
- 详细信息,例如帧的
id和name,以帮助识别 HTML 中的<iframe>。
注意: 历史上,已废弃的 PerformanceNavigation.type 属性用于监控 bfcache,开发者通过测试 type 为 "TYPE_BACK_FORWARD" 来获取 bfcache 命中率的指示。然而,这并未提供任何 bfcache 阻塞的原因或其他数据。今后应使用 notRestoredReasons 属性来监控 bfcache 阻塞。
记录 bfcache 阻塞原因
可以使用 PerformanceObserver 获取持续的 bfcache 阻塞数据,如下所示:
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 阻塞数据。
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 对象记录到控制台。这些对象具有以下结构,代表了顶级帧的阻塞状态:
{
"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 属性中,表示与每个嵌入帧相关的阻塞原因。
例如
{
"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 阻塞。不包含阻塞原因或有关子树较低级别的信息(即使某些子级别是同源的)。
例如
{
"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"-
文档通过调用
SyncManager的register()方法、PeriodicSyncManager的register()方法或BackgroundFetchManager的fetch()方法请求了后台工作。 "broadcastchannel-message"-
当页面存储在前进/后退缓存中时,页面上的
BroadcastChannel连接接收到一条消息以触发message事件。 "idbversionchangeevent"-
在卸载过程中,文档有一个待处理的
IDBVersionChangeEvent。 "idledetector"-
在卸载过程中,文档有一个活动的
IdleDetector。 "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"-
在卸载过程中,一个
RTCPeerConnection或RTCDataChannel被关闭,因此页面未处于可以存储在前进/后退缓存中的状态。 "sensors"-
文档请求了传感器访问权限。
"serviceworker-added"-
当页面位于前进/后退缓存中时,文档的服务工作客户端开始被 服务工作器 控制。
"serviceworker-claimed"-
当页面位于前进/后退缓存中时,文档的服务工作客户端的活动 服务工作器 被声明。
"serviceworker-postmessage"-
当页面位于前进/后退缓存中时,文档的服务工作客户端的活动 服务工作器 收到了一条消息。
"serviceworker-version-activated"-
当页面位于前进/后退缓存中时,文档的服务工作客户端的活动 服务工作器 的版本被激活。
"serviceworker-unregistered"-
当页面位于前进/后退缓存中时,文档的服务工作客户端的活动 服务工作器 的服务工作器注册被注销。
-
此文档位于
SharedWorkerGlobalScope的所有者集中。 "smartcardconnection"-
在卸载过程中,文档有一个活动的
SmartCardConnection。 "speechrecognition"-
在卸载过程中,文档有一个活动的
SpeechRecognition。 "storageaccess"-
文档通过使用 Storage Access API 请求了存储访问权限。
"unload-listener"-
文档为
unload事件注册了一个事件监听器。 "video-capture"-
文档通过使用 Media Capture and Streams 的
getUserMedia()并带有视频,请求了视频捕获权限。 "webhid"-
文档调用了 WebHID API 的
requestDevice()方法。 -
文档使用了 Web Share API 的
navigator.share()方法。 "webtransport"-
在卸载过程中,一个打开的
WebTransport连接被关闭,因此页面未处于可以存储在前进/后退缓存中的状态。 "webxrdevice"-
文档创建了一个 XRSystem。
浏览器兼容性
加载中…
另见
注意: 本文改编自 Chris Mills 和 Barry Pollard 的 前进/后退缓存 notRestoredReasons API,最初于 2023 年在 developer.chrome.com 上发布,采用 知识共享署名 4.0 许可。