使用应用指标衡量后退/前进缓存
bfcacheMetricsIntegration 将 3 个 bfcache 信号转为应用指标,已在 JavaScript SDK v11 上线,可追踪返回键的瞬时体验。
中文
复制

这个盲区正是我们要攻克的目标。上线应用指标之后,我们终于能把任何叫得出名字的浏览器信号变成一个可以画成图表的数字,于是我们开始寻找那些重要、却从未在其他遥测数据类型(比如 span)中浮现的信号。bfcache 排在第一位。成果就是新的 bfcacheMetricsIntegration。它把浏览器每天几乎悄无声息地做出数千次的一个决定,变成了可以度量、可以追踪、可以修复的东西。
bfcache 如何影响关键体验
离开一个页面时,浏览器通常会把它销毁。脚本停止,内存释放,DOM 不复存在。之后再回来,页面从头重建,监听器重新触发,fetch 请求四散发出,加载动画转个不停。
bfcache 把这些全部短路。浏览器把页面原地冻结,在内存中保留一份完整快照:DOM、JavaScript 堆、滚动位置,一切都在。
发生前进或后退导航时,浏览器把这份快照解冻,直接交还。切换实际上是瞬时的,因为没有任何东西需要加载。页面从未真正消失。
这不是什么小众优化。前进和后退导航是 Web 上频率最高的路径之一,而在那些感知速度能转化为商业价值的流程里,它们占据主导地位。比如电商中商品页与列表页之间的来回跳转、搜索结果页的反复切换、文档站、媒体浏览、交易市场。对于网络或设备较慢的用户,收益也最大——否则他们每次按返回键都要付出一次完整重新加载的代价。在这些路径上,“返回键有多快”应该是一等的性能问题,而且往往比全新页面加载更重要。
两者的差别并排对比最容易感受到。未命中意味着从头重建整个页面,重新请求,重新渲染,并且丢失用户原来的位置。
更多在于发现
所有性能工具都围绕页面加载生命周期构建,Sentry 也不例外。对常规工具来说,bfcache 命中与什么都没发生毫无区别,所以前进/后退导航有时根本不会被察觉。
这里有个悖论。bfcache 工作得越好,现有工具收集到的数据就越少,因为成功意味着沉默。看不见的东西就追踪不到,而这里最健康的情况恰恰是不留痕迹(双关语)。
更麻烦的是,浏览器根据不断漂移的启发式规则决定页面是否符合 bfcache 条件,你的页面可能莫名其妙就被判定为不合格。
不合格的原因可能很平常。一个 unload 监听器、一个 Cache-Control: no-store 响应头、一个未关闭的连接,或者嵌入 iframe 里的某个拦截器,都曾把页面挡在缓存之外。等你读到这篇文章时,其中一些可能已经不再适用,新的原因可能已经取而代之。你或第三方脚本可能在毫无察觉的情况下加上其中任何一条。
所以,资格可能因为简单的代码改动而退化,也可能在代码完全没变的情况下退化。靠测试或代码审查来对抗这种退化并不可行。这本来就该被主动追踪和修复。
应用指标天然适合发现这类问题,这是 span 或日志做不到的。每当未命中激增,你可以按路由拆解,再按发布版本,再按浏览器,直到揪出元凶。每一个“那 x 呢”都只是又一次查询,不需要重新部署或添加新的埋点。
工作原理
浏览器其实已经告诉你一切了。pageshow 事件和 notRestoredReasons API 都精确描述了页面是否不符合 bfcache 条件,以及原因。
bfcacheMetricsIntegration 把这些信号建模为三个应用指标(取决于浏览器支持),每个都带一些属性:
browser.bfcache.navigation是计数器,每次前进/后退导航记一次,携带:
browser.bfcache.outcome:hit 或 miss。
browser.bfcache.not_restored_reason_count:浏览器报告的原因数量。
browser.bfcache.not_restored(仅 Chromium)是计数器,每次未命中时每个未恢复原因记一次,携带:
browser.bfcache.reason:浏览器给出的原始原因字符串,例如unload-listener、websocket、idbversionchangeevent、response-cache-control-no-store。browser.bfcache.frame:阻塞发生的位置,主文档为top,子框架为child。
browser.bfcache.reload.duration 是一个分布,单位为毫秒,表示前进/后退导航未命中时,回退重载的代价有多高。这才是真正影响指标的所在。它并不声称知道恢复本来能有多快,而是衡量未命中实际让用户付出了多少代价,这样你就可以按真实痛感来排优先级,而不是按阻塞数量。
我们最终选定这三个指标,是因为它们合起来回答了前进/后退导航的完整链条:恢复了吗?为什么没有?未命中的代价是什么?
如何使用
一行代码即可启用该集成:
import * as Sentry from "@sentry/browser";
Sentry.init({
integrations: [
// other integrations...
Sentry.bfcacheMetricsIntegration(),
],
});
该集成输出的是应用指标,所以我们不会塞给你一个只能回答部分问题、还需要不断调整的预制仪表盘,而是让你自己搭建。 借助 agentic dashboard creation,你可以用这些指标告诉它你想看到什么。下面是几个例子:
- “使用 bfcache 指标,我想看到整体的 bfcache 命中率,以及过去一个月是在上升还是下降。”
- “使用 bfcache 指标,我想看到哪些路由造成的 bfcache 未命中最多,以便优先修复它们。”
- “使用 bfcache 指标,我想看到 bfcache 未命中的主要原因,可以是全局的,也可以针对某个路由。”
你还可以继续和向导对话,不断调整仪表盘,直到满意为止。
完整的配置步骤以及指标和属性的完整列表,见 bfcacheMetricsIntegration 文档。
开始跟踪你的后退按钮
该集成需要主动开启,随 JavaScript SDK v11 发布。命中和未命中结果在现代浏览器上都可用,所以你今天就能衡量自己的 bfcache 健康状况。详细的未恢复原因目前是 Chromium 独有的增强功能,随着更多引擎实现 notRestoredReasons API,覆盖范围会自行扩大。
我们还在把 bfcache 感知扩展到那些从未统计过这类导航的功能上,比如 Core Web Vitals。恢复的页面不会触发 pageload,因此这些指标从未记录到它。接入 bfcache 正好补上这个缺口,而这类覆盖我们更愿意直接做进去,而不是留给你自己去发现。
构建 Sentry 的 Laravel AI 集成
我们在 Agent Hackweek 期间如何为 Laravel AI 打造零配置的 Agent Tracing——从 AI 生成的防御性代码,到把 LLM 调用与 HTTP 请求对应起来。阅读更多 
用 agent tracing 调试我们的 AI 搜索助手
我们团队如何用 Sentry 自家的 AI Conversations 功能来调试我们的自然语言搜索助手。阅读更多 
Application Metrics 抓到了我写错的尺寸估算器
有些数字太重要,不该埋在日志里,也不该因为采样而丢失。一个小小的视频转换器,成了我见过的用 Application Metrics 跟踪 KPI 最清楚的例子。阅读更多
收听 Syntax Podcast
我们当然会赞助一档开发者播客。去你常用的收听平台上听听看。收听 Syntax
