JavaScript 硬刷新当前页面

266

如何通过JavaScript强制网页浏览器进行硬刷新?
硬刷新意味着获取页面的新副本并更新所有外部资源(图片、JavaScript、CSS等)。


我强烈建议接受不同的答案。请查看评论和我在顶部添加的注释。 - Inigo
这个问题并没有错,但是它是13年前的了,而且很多提出的解决方案并不适用于所有平台。 - Ben in CA
我添加了悬赏,但仍然没有得到任何更新的相关答案。难道在网络标准中真的没有办法实现这样一个“简单”的任务吗? - Ben in CA
10个回答

402

⚠️ 这个解决方案并不适用于所有浏览器。关于 location.reload(),可以查看 MDN 页面

注意:Firefox 支持一个非标准的 forceGet 布尔参数,用于告诉 Firefox 跳过缓存并强制重新加载当前文档。但是,在其他所有浏览器中,您在 location.reload() 调用中指定的任何参数都将被忽略,并且不会产生任何影响。

尝试:

location.reload(true);
当这个方法接收到一个 true 值作为参数时,它将导致页面总是从服务器重新加载。如果它是 false 或未指定,则浏览器可能会从其缓存中重新加载页面。
更多信息:

26
我很确定这不会重新加载所有外部资源。在硬重载之后,你需要阅读所有alinkscriptimg元素,并在每个外部引用的结尾附加一个随机查询字符串,或者在服务器上进行此操作。请注意保持原文意思并使翻译更加通俗易懂。 - Doug Neiner
8
2010年的时候它可行吗?但是在2018年(在Chrome中)它肯定不行了。Chrome会从缓存中加载所有内容(除了/Home/Index)。火狐浏览器似乎还能用,这是怎么回事? - Maciej Szpakowski
9
对我没用,这不会清除我使用Ctrl+F5清除的数据。 - ozimax06
7
我认为这个功能在HTML5中被移除了。 - Mygod
11
现在浏览器已经不再支持和忽略它了。 - Igor Kurkov
显示剩余9条评论

10
window.location.href = window.location.href

26
如果浏览器有缓存,这将不会从服务器上拉取页面。 - LukeP
4
请在您的回答中添加一些解释,以便其他人可以从中学习。 - Nico Haase
适用于我的情况!我认为它清除了我正在开发的WordPress插件中的POST数据。 - Marina Dunst

8

以上被接受的答案在大多数现代浏览器上已经不再有效,只是普通重新加载。我已经在我最近更新的Chrome上尝试了所有这些方法,包括 location.reload(true)location.href = location.href<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />。但它们都没有起作用。

我的解决方案是使用服务器端能力将非重复查询字符串附加到所有包含的源文件引用中,例如下面的示例。

<script src="script.js?t=<?=time();?>"></script>

因此,您还需要在何时保留先前的文件和何时更新它时动态地控制它。唯一的问题是当插件通过脚本执行文件包含时,您无法控制修改它。不要担心源文件泛滥。旧文件被取消链接后,它将自动进行垃圾回收。


1
我也在为我的React应用程序寻找缓存破坏解决方案而苦苦挣扎,在前端方面似乎没有任何有效的方法,除了修改服务器资源之外,你有找到其他解决方案吗? - Mohd Maaz
这些是服务器端缓存的常见情况,例如Cloudflare缓存。脚本和资源会在其端进行缓存。在这种情况下,只需要使用上述方法。 - Lost Stranger
1
不是发帖者的错,但这并不理想。我希望到了2023年会有一种“简单”的方法来强制刷新 - 但是当我发布赏金时,我没有得到更好的答案,所以将其授予您。 - Ben in CA

8

这是一个涉及IT技术的2022年更新,包含两种方法,考虑到URL中带有 # 的SPA:

方法1:

正如其他答案中提到的一样,一种解决方案是将一个随机参数放入查询字符串中。可以使用以下javascript实现:

function urlWithRndQueryParam(url, paramName) {
    const ulrArr = url.split('#');
    const urlQry = ulrArr[0].split('?');
    const usp = new URLSearchParams(urlQry[1] || '');
    usp.set(paramName || '_z', `${Date.now()}`);
    urlQry[1] = usp.toString();
    ulrArr[0] = urlQry.join('?');
    return ulrArr.join('#');
}

function handleHardReload(url) {
    window.location.href = urlWithRndQueryParam(url);
    // This is to ensure reload with url's having '#'
    window.location.reload();
}

handleHardReload(window.location.href);

不好的是,它会改变当前的网址,在干净的网址中,这有时可能对用户来说不太美观。

方法2:

https://splunktool.com/force-a-reload-of-page-in-chrome-using-javascript-no-cache中得到灵感,该过程可以先获取没有缓存的网址,然后重新加载页面:

async function handleHardReload(url) {
    await fetch(url, {
        headers: {
            Pragma: 'no-cache',
            Expires: '-1',
            'Cache-Control': 'no-cache',
        },
    });
    window.location.href = url;
    // This is to ensure reload with url's having '#'
    window.location.reload();
}

handleHardReload(window.location.href);

这甚至可以与方法1结合使用,但我认为添加标题应该足够了:

async function handleHardReload(url) {
    const newUrl = urlWithRndQueryParam(url);
    await fetch(newUrl, {
        headers: {
            Pragma: 'no-cache',
            Expires: '-1',
            'Cache-Control': 'no-cache',
        },
    });
    window.location.href = url;
    // This is to ensure reload with url's having '#'
    window.location.reload();
}

handleHardReload(window.location.href);

嗨,Miquel。这可能是一个非常愚蠢的问题,但我要传递给这个函数的确切url和paramName是什么?它只是我的当前url吗?谢谢!url只是window.location.href,还是我需要拆分或调整它? - Powermaster Prime
url是您想要更新查询参数的URL,paramname是您想要更新的参数。例如,如果您调用urlWithRndQueryParam('https://google.com/whatever?_z=1234/#/start', '_z'),您将得到'https://google.com/whatever?_z=83234/#/start'。在stackblitz中尝试一下吧。 - Miquel
Miquel,不幸的是,似乎这些方法不会像实际的Google命令那样强制刷新CSS。有关一些可能的解决方案,您有什么建议吗?也许我没有正确地实施您的方法。 - Powermaster Prime
1
mmm @OldsDiesel 这很奇怪,pragmacache-control正是控制客户端和服务器缓存行为的标头。您能否检查一下开发工具中是否看到了带有这些标头的后台请求以及结果是什么?如果结果是未缓存的,则工作正常,可能是重新加载的问题。否则可能是fetch请求没有被发送。请注意,您应该等待handleHardReload完成:await handleHardReload(window.location.href) - Miquel
1
对于 CSS,您可以尝试使用头部脚本动态加载 CSS 脚本,并添加随机时间参数。想法是创建一个包含要加载的脚本的数组,并使用 document.createElementdocument.body.appendChild 直接注入它们。 - Miquel
1
Miquel,我们成功了!我无法感谢你提供的帮助以及回到这个旧线程进行更新。我们真的没有像你这样的社区人士,就无法完成这个任务。 - Powermaster Prime

6

使用搜索参数更改当前URL将导致浏览器将该参数传递给服务器,换句话说,会强制刷新页面。

(但如果使用Service Worker拦截,则不能保证结果。)

  const url = new URL(window.location.href);
  url.searchParams.set('reloadTime', Date.now().toString());
  window.location.href = url.toString();

如果您需要支持旧版浏览器:

if ('URL' in window) {
  const url = new URL(window.location.href);
  url.searchParams.set('reloadTime', Date.now().toString());
  window.location.href = url.toString();
} else {
  window.location.href = window.location.origin 
    + window.location.pathname 
    + window.location.search 
    + (window.location.search ? '&' : '?')
    + 'reloadTime='
    + Date.now().toString()
    + window.location.hash;
}

即便如此,强制刷新所有的 CSS 和 JS 会更加费力。你需要对所有 <script> 中的 src 属性和 <link> 中的 href 属性添加一个 searchParam 来完成相同的过程。但是需要注意的是,这种方法不会卸载当前的 JS,但对于 CSS 来说是可行的。

document.querySelectorAll('link').forEach((link) => link.href = addTimestamp(link.href));

我不会提供JS示例,因为这可能会引起问题。

当编译HTML时,在JS和CSS链接中添加一个时间戳作为搜索参数,可以避免这种麻烦。


这将在当前页面上进行无限循环刷新。 - Dan Froberg
听起来你是在脚本加载时运行它,而不是在函数内调用它。至少这表明它可以工作。 - ShortFuse
需要一个if语句。请查看我的答案:https://dev59.com/OHI95IYBdhLWcg3w5iY4#73683077 - Dan Froberg
你的答案中在脚本加载时调用了 refresh(),这就是为什么它会进入无限循环的原因。你编写的代码在加载时刷新了页面。 - ShortFuse
谢谢,我将这个附加到我的答案中:它不会出现无限循环,因为有所描述的if语句。刷新()函数可以通过按钮或其他有条件的方式调用,而不是页面加载。 - Dan Froberg

4

更新以刷新所有外部资源(图像、JavaScript、CSS等)

将此放入名为HardRefresh.js的文件中:

  function hardRefresh() {
    const t = parseInt(Date.now() / 10000); //10s tics
    const x = localStorage.getItem("t");
    localStorage.setItem("t", t);

    if (x != t) location.reload(true) //force page refresh from server
    else { //refreshed from server within 10s
      const a = document.querySelectorAll("a, link, script, img")
      var n = a.length
      while(n--) {
        var tag = a[n]
        var url = new URL(tag.href || tag.src);
        url.searchParams.set('r', t.toString());
        tag.href = url.toString(); //a, link, ...
        tag.src = tag.href; //rerun script, refresh img
      }
    }
  }

  window.addEventListener("DOMContentLoaded", hardRefresh);
  window.addEventListener("deviceorientation", hardRefresh, true);

这段代码可以强制刷新每个访问者的页面,以便任何更新都能显示而不会出现缓存问题。

重复的DOM渲染不是性能问题,因为第一次渲染是从缓存中进行的,它在<script src="js/HardRefresh.js">处停止渲染,然后重新从服务器加载页面。当它运行一个刷新后的页面时,它也会刷新页面中的URL。

上次刷新时间x存储在localStorage中。它与当前时间t进行比较,以便在10秒内进行刷新。假设从服务器加载不需要超过10秒,我们就能够停止页面刷新循环,所以不要少于10秒。

对于页面的访问者,x != t自长时间以来或首次访问即为真;这将从服务器获取页面。然后差异小于10秒,x == t,这将使else部分向hrefsrc添加查询字符串,其中包含要刷新的源。

刷新()函数可以通过按钮或其他条件方式调用。通过细化您代码中的排除和包含URL,可以实现完全控制。


虽然这样确实会生成“mypage.com/page?r=166821181”,但我的页面仍然使用缓存的图像。我还能做些什么吗? - David.P
1
@David.P 代码现已通过设置 href 更新新的查询字符串,并将重新加载。客户端还会更新 .js 和 .css 文件! - Dan Froberg
未捕获的类型错误:无法构造“URL”,无效的URL。 - Khalid Almannai
尝试简化!似乎不需要从服务器强制刷新页面。移除if语句。将document.querySelectorAll("a, link, script, img")更改为document.querySelectorAll("a")。对于css、js和资源,您可以使用带有查询的最小动态加载器。 - Dan Froberg

3
对于Angular用户和在这里找到的内容,你可以按照以下步骤进行操作:
<form [action]="myAppURL" method="POST" #refreshForm></form>

import { Component, OnInit, ViewChild } from '@angular/core';

@Component({
  // ...
})
export class FooComponent {
  @ViewChild('refreshForm', { static: false }) refreshForm: ElementRef<HTMLFormElement>;

  forceReload() {
    this.refreshForm.nativeElement.submit();
  }
}

解释:Angular

位置:reload(),Location.reload() 方法重新加载当前的 URL,就像刷新按钮一样。仅使用 location.reload() 不是解决方案,如果你想执行强制重新加载(例如,Ctrl + F5)以从服务器重新加载所有资源而不是从浏览器缓存中重新加载。解决此问题的方法是,对当前位置执行 POST 请求,因为这总是会使浏览器重新加载所有内容。

不使用 Angular

显然,“通过 POST 获取文件可以清除缓存的文件,并且似乎在任何浏览器中都有效”

因此,使用“使用 POST 的 fetch API”也应该有效


谢谢!谢谢!我尝试了所有其他的客户端解决方案,但都无法强制刷新.wasm文件!但是你的方法成功了(我没有使用Angular,而是使用了带有POST的fetch API)!通过POST方式获取文件确实清除了缓存文件,并且似乎在任何浏览器中都有效。 :) - RandomGuy
1
不客气 ;) 我已经更新了我的回答,添加了你给我的信息。 - Raphaël Balet

3

location.reload(true)已被弃用,因此没有直接的选项来进行硬刷新,但我们仍然可以手动清除缓存然后重新加载。 请使用这个自定义函数代替。

export const clearCache =  (reloadAfterClear = true) => {
    if('caches' in window){
        caches.keys().then((names) => {
            names.forEach(async (name) => {
                await caches.delete(name)
            })
        })

        if(reloadAfterClear)
            window.location.reload()
    }
}

如果您不想立即刷新并只清除缓存,请使用 clearCache(false)

希望它能对您有所帮助,因为上述选项都对我没有起作用。


1

我发现最可靠的方法是通过在查询字符串中添加一个值来使用缓存破坏器。

这是我使用的通用例程:

    function reloadUrl() {

        // cache busting: Reliable but modifies URL
        var queryParams = new URLSearchParams(window.location.search);
        queryParams.set("lr", new Date().getTime());        
        var query = queryParams.toString();                
        window.location.search = query;  // navigates
    }

调用此函数将产生类似于这样的结果:
https://somesite.com/page?lr=1665958485293

重新加载后。

这种方法可以强制每次重新加载,但缺点是URL会改变。在大多数应用程序中,这并不重要,但如果服务器依赖于特定参数,则可能会导致潜在的副作用。


虽然这确实会生成“https://mypage.com/page?lr=1665958485293”,但我的页面仍然使用缓存的图像。现在该怎么办? - David.P
这个方法是否会“刷新”React SPA的任何缓存JS内容? - Ben in CA
1
@David.P ... 这是因为这些图片都是自己的资源,有着自己的URL。你需要解析文档中的所有图片,并对它们的URL添加随机化处理。可以参考以上 Dan Frodberg 的回答。 - eidylon
谢谢。前段时间我发现了一个简单的JavaScript方法,可以在页面加载后强制重新加载所有图像。我一旦找到它,就会在这里发布该方法。 - David.P

1
接受的答案 location.reload(true); 不起作用。原因如下(摘自 MDN 页面):

注意:Firefox 支持 location.reload() 的非标准 forceGet 布尔参数,告诉 Firefox 绕过其缓存并强制重新加载当前文档。但是,在所有其他浏览器中,您在 location.reload() 调用中指定的任何参数都将被忽略,并且不会产生任何效果。

尽管如此,您可能会在现有代码中遇到 location.reload(true) 的实例,这些代码是按照所有浏览器都会产生强制重新加载效果的假设编写的。GitHub 上的“location.reload(true)”搜索返回了数十万个结果。因此,有很多现有的代码具有此功能。
它的历史是:某个版本的 Netscape Navigator 添加了对它的支持,显然最终被 Firefox 接管了。在某个时候,W3C Web APIs 工作组提出了一个问题,考虑将其添加到 location.reload() 的规范中。但是,它实际上从未被添加。
因此,布尔参数不是 location.reload() 当前规范的一部分 - 实际上,它从未成为任何已发布的 location.reload() 规范的一部分。

来源: https://developer.mozilla.org/zh-CN/docs/Web/API/Location/reload

那么,如何强制刷新呢? 有两个想法:

  1. 使用查询字符串,如本主题中多个其他答案所建议/解释/演示的;

  2. 向W3C发送请求。
    https://www.w3.org/Consortium/contact

(如果几年前广泛采用了建议#2,我们几年前就可以停止使用查询字符串hack了。)


查询字符串方法是否会“刷新”React SPA的任何缓存JS内容?除非Webpack以不同的名称命名文件,我想是不会的。 - Ben in CA

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