toc
为什么使用 bfcache
在 Firefox 1.5 版本的时候首次引入了 bfcache
,它的主要目的是为了优化用户体验。在用户点击后退或者前进按钮时,浏览器会尝试从缓存中恢复页面,而不是重新加载页面。这可以减少用户的等待时间,提高用户的响应速度。bfcache 在移动端的页面中会比较常见,因为移动端设备的性能相对较低,网络情况也不如 PC 那样稳定,bfcache 可以帮助我们减少页面的加载时间,在不同的页面之间切换也会更快,因为启用了缓存的页面再次访问时不需要重新加载。
怎么判断页面是否进入了 bfcache
在页面中 pageshow
事件都会在 load 事件之后立即触发。pageshow
事件有一个 persisted
属性,如果网页是从 bfcache 恢复的,该属性为 true
,否则为 false
。您可以使用 persisted
属性来区分常规网页加载和 bfcache 恢复。例如:
而当页面卸载或浏览器尝试将其放入 bfcache 时,都会触发 pagehide
事件。pagehide
事件也有一个 persisted
属性。如果值为 false
,您可以确信相应网页不会进入 bfcache。不过,persisted
为 true
并不能保证网页会被缓存。这意味着浏览器打算缓存网页,但可能有其他因素导致无法缓存。
如何阻止页面进入 bfcache
- 页面使用 unload 或者 beforeunload 处理程序;
- 为确保您的网站未使用卸载处理程序,例如防止扩展程序添加 unload 事件,可以声明
Permissions-Policy: unload=()
HTTP 响应标头。
- 为确保您的网站未使用卸载处理程序,例如防止扩展程序添加 unload 事件,可以声明
- 页面设置 “cache-control: no-store”.
- 页面是 HTTPS 同时页面至少有一个以下设置:
- “Cache-Control: no-cache”
- “Pragma: no-cache”
- 使用 “Expires: 0” 或者 “Expires” 设置相对于“Date”header 值的过去日期值 (除非指定 “Cache-Control: max-age=”);
- 当用户导航跳离页面时页面还没有完全加载或者因为其他原因有等待(pending)的网络请求 (例如 XMLHttpRequest));
- 页面运行 IndexedDB 事件;
- 顶级页面包含 frames (例如
<iframe>
) 因为这里列出的任何原因 而没有被缓存; - 页面是在 frame 内而且用户在这个框架中加载一个新页面(在这种情况下,当用户离开这个页面,最后加载入 frames 的内容会被缓存)。
利用 bfcache 优化网页
并非所有页面都存储在 bfcache 中,即使某个页面已存储在 bfcache 中,它也不会无限期地保留在此处。浏览器将 bfcache 中的页面保留在内存中,直到达到某个限制,然后开始逐出页面。
使用 pagehide, 而不是 unload
在所有浏览器中针对 bfcache 进行优化的最重要方法是一律不使用 unload
事件监听器。应该监听 pagehide,因为它在网页进入 bfcache 时和在 unload 每次触发时都会触发。
unload 是一项较旧的功能,最初设计是为了在用户离开某个页面时触发。但现在不是了,许多网页仍会假设浏览器以这种方式使用 unload,并且在 unload 触发后,未加载的网页将继续请求。如果浏览器尝试缓存未加载的网页,这可能会破坏 bfcache。
有条件性地增加 beforeunload 事件监听
beforeunload 事件不一定会导致您的网页不符合启用 bfcache 条件。但万事无绝对,所以建议仅在绝对必要的情况下使用,比如满足某些条件的情况下才增加事件监听。beforeunload 的一个示例用例是警告用户,如果他们离开页面,将丢失未保存的更改。在这种情况下,我们建议仅在用户有未保存的更改时添加 beforeunload 监听器,然后在保存未保存的更改后立即移除这些监听器,如以下代码所示:
避免 window.opener 引用
在旧版浏览器中,如果页面是使用包含 target=_blank 的链接中的 window.open() 打开的,未指定 rel=“noopener”,则打开的页面会引用所打开页面的窗口对象。
除了存在安全风险之外,具有非 null window.opener 引用的页面无法安全地放入 bfcache 中,因为这可能会破坏任何尝试访问该页面的页面。
为避免这些风险,请使用 rel=“noopener” 来阻止创建 window.opener 引用。这是所有现代浏览器中的默认行为。如果您的网站需要打开一个窗口并使用 window.postMessage() 或通过直接引用 window 对象对其进行控制,则打开的窗口和打开器都不符合 bfcache 的条件。
最后
其实通篇看下来,bfcache 也不是什么特别高级的东西,但它确实可以提升一些性能。所以,在需要的时候,还是可以考虑使用一下的,尤其是在一些对性能要求比较高的系统中,比如个人博客,又或者是公司的业务系统。后期优化其实很难,所以这种优化工作应该从一开始就去做,这样即便后期想要做优化,成本也会小很多。
以上。