Safari bfcache(Back-Forward Cache) 特性

Safari 有一个关于缓存的特性,在页面前进、后退时,会缓存之前的页面,并在回到原来的页面时使用缓存,也就是说页面不会重新加载。而在 Android 当中,不同浏览器或 APP 的行为表现并不一致,通过 location.href = newURL 或者 location.assign() 跳转到新的页面后,再次返回时,微信会刷新页面,而实测在 MIUI 自带浏览器、夸克浏览器都表现为页面会被缓存。

在实际开发中需要特别注意该行为,以防止产生一些问题,比较常见的例如对接支付平台、或跳转到新页面支付填写表单(登录、注册、登记等),然后返回到原来的页面,这时候会非常依赖这个事件来更新状态。

示例:

1
2
3
4
5
6
7
8
9
10
11
// 按钮点击跳到注册页面
document.querySelector('.btn-register').addEventListener('click', () => {
toast.loading() // 展示 loading 动画
location.assign('url/to/register') // 跳转页面
}, false)

// Safari 返回到当前页面
window.addEventListener('pageshow', async () => {
await refreshUserInfo() // 刷新用户状态
toast.clear() // 清除 loading 动画
})

上面的示例当中如果你不注册 pageshow 事件,那么 loading 动画和状态在返回时不会刷新。

此外,还需要注意的是,当页面打开时,也会触发 pageshow 事件。以上面的代码为例,当你在页面初始化的时候刷新了用户状态,这时候将产生两个相同的请求,影响应用性能增加资源消耗,并且没有实际意义。

这时候你可能需要 PageTransitionEventpersisted 属性,它标记了 pageshowpagehide 时,页面是否是从缓存当中加载的。在初次打开或者刷新页面时 persistedfalse,所以上面的代码我们可以这么优化一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 按钮点击跳到注册页面
document.querySelector('.btn-register').addEventListener('click', () => {
toast.loading() // 展示 loading 动画
location.assign('url/to/register') // 跳转页面
}, false)

// Safari 返回到当前页面
window.addEventListener('pageshow', async evt => {
// 只从缓存中加载页面时,就不会和页面初始化的时候出现重复了
if (evt.persisted) {
await refreshUserInfo() // 刷新用户状态
toast.clear() // 清除 loading 动画
}
})

演示

你可以在手机或其他设备打开下面这个页面,测试了解一下详细的事件生命周期:

 打开

手机可以直接扫码:

getting demo

参考: