用户说页面卡,我盯了三年 Lighthouse——今天发现根子不在代码,在浏览器的「后退」键里
用户说页面卡,我盯了三年 Lighthouse——今天发现根子不在代码,在浏览器的「后退」键里
你有没有遇到过这种情况:用户从列表页点进详情页,LCP 1.8 秒,绿的;用户按后退键回到列表页,LCP 变成了 0.4 秒,又绿了;然后用户再点进去,LCP 变成 2.3 秒,红了。
你第一反应是「详情页图片太大」「列表页缓存有问题」,查了半天什么都没查到。
今天这个坑的根子不在你的代码,在浏览器的 bfcache 里面。
bfcache 是什么
bfcache(Back-Forward Cache)是浏览器的一个优化策略:用户点后退键时,浏览器不是重新加载页面,而是把整个页面状态(包括 DOM、JS 堆、滚动位置)从内存里直接恢复出来。相当于「暂停」而非「刷新」。
这个优化对用户体验是实打实的——后退操作可以快到 0 毫秒。但它对你的监控体系来说,是一个隐藏的炸弹。
指标被偷偷重置了
当页面从 bfcache 恢复时,Chrome 会在内部触发一个「伪导航」:它重置 LCP 计时器、INP 观测器、CLS 累计窗口。换句话说,浏览器认为这是一次全新的页面访问,之前的性能数据全部清零。
但这次「伪导航」并不会重新发起网络请求。图片、字体、接口——能读缓存的读缓存,能用内存的用内存。用户体验确实快,但快的原因是「恢复」而非「加载」。
这就是为什么你的监控数据里,后退回来的页面 LCP 异常低——它根本就没在测加载,它测的是从内存里恢复出来的速度。这个数字没有任何优化参考价值,反而会把你带偏。
怎么判断用户在用 bfcache
pageshow 事件会告诉你答案:
窗口.addEventListener pageshow 事件,若页面是来自 bfcache 恢复的,event.persisted === true。这个判断是跨浏览器的,Chrome/Firefox/Safari 都支持。
三个常见误区
第一个误区是「不管 bfcache」。很多监控 SDK 在初始化时没有区分正常加载和 bfcache 恢复,导致 LCP 数据一半是真实加载、一半是缓存恢复,混在一起没法看。
第二个误区是「直接刷新」。有人发现 bfcache 数据异常,选择直接 location.reload() 跳过缓存。这样用户体验反而变差了——用户按后退键本来是想快,结果变成了等三秒重新加载。正确的做法是过滤掉这些异常数据点,而不是让用户承担后果。
第三个误区是「忽略 INP」。bfcache 恢复时 INP 的交互记录也会被清零,如果用户刚恢复页面就做了一次点击,你可能根本捕捉不到这次交互,导致 INP 数据偏低——和 LCP 偏低的原因一模一样。
实战处理方案
生产环境里,核心是在监控 SDK 层面识别并过滤 bfcache 恢复的数据:
注册 Service Worker 时主动通知页面状态,监听 message 事件判断 bfcache-restored,然后通知所有客户端重置性能观测器。
如果你的监控是基于 PerformanceObserver 的,可以在 bfcache 恢复时主动 disconnect 并重新连接观测器,确保下一次用户交互能被正确记录:
let lcpObserver = null;
function resetLcpObserver() {
if (lcpObserver) {
lcpObserver.disconnect();
}
lcpObserver = new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
console.log("LCP:", lastEntry.startTime);
});
lcpObserver.observe({ type: "largest-contentful-paint", buffered: true });
}
window.addEventListener("pageshow", (e) => {
if (e.persisted) {
resetLcpObserver();
}
});
如果你用的是 Google Chrome 的 performance.resetLcpThreshold() 这个实验性 API,它本来就是设计用来解决 bfcache 场景下 LCP 阈值被提前重置的问题——但前提是你要先正确识别了 bfcache 恢复这个场景。
怎么验证你的监控有没有中招
Chrome DevTools 的 Application 面板里有一个「Back/forward cache」测试按钮。点击之后,Chrome 会尝试把你的页面放进 bfcache,然后执行一次后退操作,告诉你页面是否能被正确缓存、如果不行的原因是什么。
这个测试跑一遍,你大概就能知道你的页面有多少比例的「后退」操作是走缓存恢复的。如果这个比例很高(比如超过 30%),而你没做过滤,那你的 LCP 数据大概率是偏低的。
下一步
第一,先跑一遍 DevTools 的 bfcache 测试,把能进 bfcache 的页面比例摸清楚。第二,检查你的监控 SDK 是否在 pageshow 事件里做了 persisted 判断,没有的话补上。第三,如果你的 RUM 平台支持在数据层面排除 bfcache 恢复的数据,现在就加上这个过滤条件。
用户体验变快了是好事,但你得确保你的监控看到的是真实情况,而不是浏览器优化带来的假象。
评论区
登录后可评论。