当点击返回按钮时,防止Safari从缓存中加载页面

101

当点击后退按钮时,Safari会加载旧的YouTube视频,我遇到了这个问题。我尝试在body标签中添加onunload =""(在这里提到:Preventing cache on back-button in Safari 5),但在这种情况下它不起作用。

有没有办法防止Safari在特定页面上从缓存中加载?


可能是[Mobile Safari后退按钮]的重复问题(https://dev59.com/RGct5IYBdhLWcg3wmOZN)。 - Mika Tuupola
@MikaTuupola的回答应该被标记为正确答案。 - Julien Bérubé
这是我也因为Safari的相同BFCache行为而遇到的问题:https://dev59.com/aJzha4cB1Zd3GeqPI7K0#40896361?noredirect=1#comment69043078_40896361 - juanmirocks
在我的情况下,当我只需要操作一些类时,这篇文章中的代码就可以工作。 - Barnee
9个回答

237
您的问题是由于回溯缓存引起的。当用户导航离开页面时,这个缓存会保存页面的完整状态。当用户使用后退按钮返回时,页面可以很快地从缓存中加载。这与仅缓存HTML代码的普通缓存不同。
当页面被加载到回溯缓存中时,onload事件将不会被触发。相反,您可以检查onpageshow事件的persisted属性。在初始页面加载时,它被设置为false。当从回溯缓存中加载页面时,它被设置为true。
解决方案是,在页面从回溯缓存中加载时强制重新加载。
window.onpageshow = function(event) {
    if (event.persisted) {
        window.location.reload() 
    }
};

如果您正在使用jQuery,请执行以下操作:

$(window).bind("pageshow", function(event) {
    if (event.originalEvent.persisted) {
        window.location.reload() 
    }
});

9
到目前为止,这是我找到的唯一可行的解决方案,但仍然不太完美,因为页面在重新加载之前仍会短暂地显示,并且该解决方案很容易被禁用JavaScript来挫败。有人找到了更完整的解决方案吗? - lukens
对于IE 11,我不得不使用window.location = window.location而不是window.location.reload(),原因我认为已经被时间的迷雾所遗忘。 - Paul D. Waite
为了解决在onpageshow事件触发之前页面可见性的问题,您可以使用onbeforeunload事件。在此事件期间,您可以将body的不透明度更改为0。是的,但页面会短暂地可见,但不透明度为0。(window.onbeforeunload =()=>{document.body.opacity = 0;})。这适用于Safari 11。 - Kamen Stoykov
1
@RainCast 你的意思是最新版本的Safari修复(们)不再起作用了,还是原始问题已经无法再现? - natonomo
显示剩余3条评论

16

所有这些答案都有点儿不正规。在现代浏览器(Safari)中,只有 onpageshow 解决方案有效。

window.onpageshow = function (event) {
    if (event.persisted) {
        window.location.reload();
    }
};

但在较慢的设备上,有时您会在重新加载之前看到上一个缓存视图闪现。解决此问题的正确方法是在服务器响应中将Cache-Control设置为以下值:

'Cache-Control', 'no-cache, max-age=0, must-revalidate, no-store'


谢谢,头部看起来很有效,并且比JS解决方案好很多。 - Andy
我将这段代码放在HEADER中,现在iPhone上的Safari通过后退按钮返回到此页面时会执行JS和JQuery代码,谢谢! - Al-Noor Ladhani
设置Cache-Control是唯一在Chrome上有效的方法。不需要所有的指令,因为这等同于Cache-Control: no-store。请参考https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#preventing_storing。 - Nathan

7

是的,Safari浏览器处理后退/前进按钮缓存与Firefox和Chrome不同。特别是像vimeo或youtube视频这样的iframes几乎没有被缓存,尽管有一个新的iframe.src。

我找到了三种方法来处理这个问题,请根据您的情况选择最好的解决方案。在Firefox 53和Safari 10.1上测试了这些解决方案。

1. 检测用户是否使用后退/前进按钮,然后重新加载整个页面或通过替换src仅重新加载已缓存的iframes

if (!!window.performance && window.performance.navigation.type === 2) {
            // value 2 means "The page was accessed by navigating into the history"
            console.log('Reloading');
            //window.location.reload(); // reload whole page
            $('iframe').attr('src', function (i, val) { return val; }); // reload only iframes
        }

如果页面已被缓存,请重新加载整个页面

window.onpageshow = function (event) {
        if (event.persisted) {
            window.location.reload();
        }
    };

3. 从历史记录中删除页面,使用户不能通过后退/前进按钮再次访问该页面

$(function () {
            //replace() does not keep the originating page in the session history,
            document.location.replace("/Exercises#nocache"); // clear the last entry in the history and redirect to new url
        });

3

我曾经遇到过相同的问题,使用了3个不同的锚点链接到下一页。当从下一页返回并选择不同的锚点时,链接没有改变。

所以我做了以下操作:

<a href="https://www.example.com/page-name/#house=house1">House 1</a>
<a href="https://www.example.com/page-name/#house=house2">View House 2</a>
<a href="https://www.example.com/page-name/#house=house3">View House 3</a>

改为

<a href="/page-name#house=house1">House 1</a>
<a href="/page-name#house=house2">View House 2</a>
<a href="/page-name#house=house3">View House 3</a>

也用于安全方面:

// Javascript
window.onpageshow = function(event) {
    if (event.persisted) {
        window.location.reload() 
    }
};

// JQuery
$(window).bind("pageshow", function(event) {
    if (event.originalEvent.persisted) {
        window.location.reload() 
    }
});

在网上找到的卸载、重新加载和单独重新加载(true)解决方案都没有起作用。希望这可以帮助到遇到同样情况的人。


2
这个行为与Safari的“前进/后退”缓存有关。您可以在相关的苹果文档中了解相关信息:http://web.archive.org/web/20070612072521/http://developer.apple.com/internet/safari/faq.html#anchor5 苹果公司的修复建议是在您的页面上添加一个空的iframe。
<iframe style="height:0px;width:0px;visibility:hidden" src="about:blank">
    this frame prevents back forward cache
</iframe>

之前被接受的答案也是有效的,我只是想加入文档和另一个潜在的解决方案。


我找到的最佳解决方案在这里:https://dev59.com/TGEi5IYBdhLWcg3w2PSB - i_a

2
您可以使用锚点,并观察文档位置href的值;
http://acme.co/开始,将一些内容附加到位置,例如“#b”;
现在您的URL是http://acme.co/#b,当用户点击返回按钮时,它会返回到http://acme.co,并且间隔检查函数会看到我们设置的哈希标签缺失,清除间隔,并加载附加了时间戳的引用URL。
这样做会有一些副作用,但我会让您自己解决 ;)
<script>
document.location.hash = "#b";
var referrer = document.referrer;

// setup an interval to watch for the removal of the hash tag
var hashcheck = setInterval(function(){
    if(document.location.hash!="#b") {

    // clear the interval
    clearInterval(hashCheck);

    var ticks = new Date().getTime();
    // load the referring page with a timestamp at the end to avoid caching
    document.location.href.replace(referrer+'?'+ticks);
    }
},100);
</script>

这段代码尚未经过测试,但只需要最小限度的调整即可正常工作。


3
有趣的是,唯一仍然有效的解决方案却得到了负面评价。 - daslicht

0

有很多方法可以禁用 bfcache。最简单的方法是设置一个“unload”处理程序。我认为让“unload”和“beforeunload”处理程序禁用 bfcache 是一个巨大的错误,但这就是他们所做的(如果你想要其中一个处理程序并且仍然使 bfcache 工作,你可以在 beforeunload 处理程序中删除 beforeunload 处理程序)。

window.addEventListener('unload', function() {})

在此处阅读更多:

https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/1.5/Using_Firefox_1.5_caching


0

首先在您的代码中插入字段:

<input id="reloadValue" type="hidden" name="reloadValue" value="" />

然后运行jQuery:

jQuery(document).ready(function()
{
        var d = new Date();
        d = d.getTime();
        if (jQuery('#reloadValue').val().length == 0)
        {
                jQuery('#reloadValue').val(d);
                jQuery('body').show();
        }
        else
        {
                jQuery('#reloadValue').val('');
                location.reload();
        }
});

0
其他解决方案应该以真实的方式工作,否则您可以使用下面的代码片段。
window.addEventListener('pageshow', (event) => {
    if (event.persisted) {
        if(!navigator.userAgent.includes('Chrome') && navigator.userAgent.includes('Safari')){
            window.location.reload();
        }
    }
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接