如何强制客户端刷新JavaScript文件?


595

我们目前正处于非公开Beta测试阶段,因此仍在进行相当快速的更改,尽管显然随着使用量的增加,我们将放慢这个过程。话虽这么说,我们遇到的一个问题是,在我们推出新JavaScript文件的更新之后,客户端浏览器仍然使用文件的缓存版本,而他们看不到更新。显然,在技术支持电话上,我们可以简单地通知他们进行ctrlF5刷新,以确保他们从服务器获取最新文件,但是最好在此之前进行处理。

我们当前的想法是将版本号简单地附加到JavaScript文件的名称上,然后在进行更改时增加脚本上的版本并更新所有引用。这肯定可以完成工作,但是更新每个发行版上的引用可能会很麻烦。

因为我确定我们不是第一个处理此问题的人,所以我认为我会将它扔给社区。更新代码时,如何确保客户端更新其缓存?如果您使用的是上述方法,是否正在使用简化更改的过程?


Answers:


529

据我所知,一个常见的解决方案是?<version>在脚本的src链接中添加一个。

例如:

<script type="text/javascript" src="myfile.js?1500"></script>

我认为此时,在所有脚本标签中没有比找到替换增加这些“版本号”更好的方法了吗?

您可能有一个版本控制系统为您执行此操作?例如,大多数版本控制系统都有一种在签入时自动注入修订号的方法。

它看起来像这样:

<script type="text/javascript" src="myfile.js?$$REVISION$$"></script>

当然,也有总是喜欢更好的解决方案这一个


5
有谁知道IE7是否忽略了这一点?当我在IE8可比性视图中进行测试时,似乎正在忽略附加的数据并使用缓存的文件。
Shane Reustle,2011年

4
我一直都知道查询字符串是键值对,如?ver = 123。谢谢!:)
Ankur-m

6
我认为这不是关于更高或更低的版本号,而是关于将附加变量值更改为浏览器尚未缓存的内容。
Max Girkens

2
我们最近遇到了相同的问题,我能想到的最好的办法是附加一个简单的函数“?mod = 123456”,其中123456是文件上修改日期的unix时间戳。这似乎可以解决问题,同时仍允许在适当的地方进行缓存。但是,我仍然看到浏览器完全忽略该指令并仍然使用旧的JS,但是我不知道有一个优雅的“完整解决方案”。
VPel 2013年

31
为了提高意识:这被认为是黑客。该方法欺骗浏览器以为正在指定一个新文件,因为它只是查看完整的文件名而不解释它。foo.js?1与的名称不同foo.js?2,因此浏览器会认为它们是两个不同的文件。缺点是两个文件将同时存在于用户的缓存中,占用了不必要的空间。
李·怀特

88

将当前时间附加到URL确实是一种常见的解决方案。但是,如果需要,也可以在Web服务器级别进行管理。可以将服务器配置为发送JavaScript文件的不同HTTP标头。

例如,要强制将文件缓存不超过1天,您可以发送:

Cache-Control: max-age=86400, must-revalidate

对于Beta版,如果要强制用户始终获取最新版本,则可以使用:

Cache-Control: no-cache, must-revalidate

3
您能更具体一点吗?
克雷克(Kreker)2012年

6
他正在谈论Web服务器为每个文件发送的标头。例如,应在Apache中进行配置。我认为这将是最好的选择
Pierre de LESPINAY 2012年

1
您在哪里配置?
迭戈

2
对于开发Web应用程序,这可能是一个很好的解决方案。对于不想永久使缓存无效的生产站点,这不是一个好的解决方案,除非您知道每个目标客户端浏览器都已到达该站点。它使我想到了潜在的Web服务器功能:根据配置的部署日期调整max-age参数。那将是真棒。
克劳德·布里森

Chrome需要这些设置才能正确缓存。如果没有它们,Chrome将永远缓存文件。Mozilla使用更合理的默认值。看到更多在: agiletribe.wordpress.com/2018/01/29/caching-for-chrome
AgilePro '18

42

Google Page-Speed:在URL中不要包含用于静态资源的查询字符串。大多数代理(最著名的是通过3.0版的Squid up)不使用“?”缓存资源。即使响应中存在Cache-control:公共标头,也不会在其URL中显示。要为这些资源启用代理缓存,请从对静态资源的引用中删除查询字符串,然后将参数编码为文件名本身。

在这种情况下,您可以将版本包含在URL中,例如:http : //abc.com/ v1.2 /script.js并使用apache mod_rewrite将链接重定向到http://abc.com/script.js。更改版本时,客户端浏览器将更新新文件。


我试过了吗?解决方案,并在IE8中,我收到了JavaScript错误。Mod重写是一个选项,但是在大多数情况下,我们对服务器没有太多控制权。我希望将版本附加到js文件本身中,或为每个版本添加一个文件夹
Karthik Sankar,2014年

@HắcHuyềnMinh:但是,当应重新加载脚本时,不应从代理缓存中重新加载脚本...
Stefan Steiger

34

此用法已被弃用:https : //developer.mozilla.org/en-US/docs/Web/HTML/Using_the_application_cache

这个答案只晚了6年,但是我在很多地方都看不到这个答案。HTML5引入了用于解决此问题的应用程序缓存。我发现我正在编写的新服务器代码使存储在人们浏览器中的旧javascript崩溃,因此我想找到一种使它们的javascript过期的方法。使用如下清单文件:

CACHE MANIFEST
# Aug 14, 2014
/mycode.js

NETWORK:
*

并在每次您希望用户更新其缓存时使用新的时间戳生成该文件。附带说明一下,如果添加了此选项,则在清单通知您之前,浏览器将不会重新加载(即使当用户刷新页面时)。


只要您记得更新清单文件,此解决方案就很好:)
Mattis

24
因为这个功能已经从Web标准被删除,请阅读文档developer.mozilla.org/en-US/docs/Web/HTML/...
弗拉维亚Obreja

1
FWIW,我最终没有使用此解决方案。使用/维护该?<version> 方法要容易得多。
amos

28

如何将filesize作为加载参数添加?

<script type='text/javascript' src='path/to/file/mylibrary.js?filever=<?=filesize('path/to/file/mylibrary.js')?>'></script>

因此,每次更新文件时,“ filever”参数都会更改。

当您更新文件并且更新结果具有相同的文件大小时如何?几率是多少?


4
这使用PHP标记,如果使用PHP标记,的确是个好主意。
Jerther '17

1
我认为添加changeate会比filesize更好:)
Mazz

2
我最初的想法是添加文件的哈希而不是版本。
Mike Cheel

我假设添加时间戳Unix也可以,对吗?例如'... file.js?filever = <?= time()?>
garanda

3
使用filemtime($ file)输出文件的时间戳,使用time()您不能使用缓存,因为它每秒钟更改一次。
neoteknic

19

并非所有浏览器都以“?”缓存文件 在里面。为了确保尽可能多地对其进行缓存,我将版本包含在文件名中。

所以stuff.js?123我代替了stuff_123.js

我曾用mod_redirect(我认为)have stuff_*.jsstuff.js


您能否详细说明一下使用mod_redirect在.htaccess中进行的操作?
Venkat D.


3
如果您可以.htaccess在答案中包含代码以供将来参考,那将是很好的。
Fizzix '16

1
哪些浏览器不使用“?”缓存文件?在里面?
rosell.dk

13

对于ASP.NET页面,我正在使用以下内容

之前

<script src="/Scripts/pages/common.js" type="text/javascript"></script>

之后(强制装弹)

<script src="/Scripts/pages/common.js?ver<%=DateTime.Now.Ticks.ToString()%>" type="text/javascript"></script>

添加DateTime.Now.Ticks效果很好。


31
这违反了客户端上的所有缓存机制。。虚拟参数应该类似“{主要版本} _ {} minor_version _ {} BUILD_NUMBER _ {}修订这将是每一个版本的唯一替代
Tohid

14
尽管这可能是开发环境中的最佳解决方案,但它不适合生产。每次为该文件加载页面时,这将完全禁用高速缓存。想象一下每天有1个50Kb文件加载10k页面,它每天代表500Mb Javascript文件。
PhilDulac '16

@PhilDulac您可以将其从Ticks更改为返回一天的字符串值,例如月份,月份或月份的星期几。最终,它只是向您展示如何使用?v方法
Alex

3
@alex确实。我只是想警告一下,如果答案中演示的用法可用于生产,则其影响可能不会在开发中显示。
PhilDulac

2
确保每天加载一次新副本的一种可能方法是使用'<script src =“ / Scripts / pages / common.js?ver <%= DateTime.Now.ToString(” yyyyMMdd“)%>”类型=“ text / javascript”> </ script>”。因此,它在一天开始时加载一次,然后进行缓存。
罗布·萨德勒

7

对于ASP.NET,我认为带有高级选项(调试/发布模式,版本)的下一个解决方案:

通过以下方式包含的Js或Css文件:

<script type="text/javascript" src="Scripts/exampleScript<%=Global.JsPostfix%>" />
<link rel="stylesheet" type="text/css" href="Css/exampleCss<%=Global.CssPostfix%>" />

Global.JsPostfix和Global.CssPostfix是通过Global.asax中的以下方法计算的:

protected void Application_Start(object sender, EventArgs e)
{
    ...
    string jsVersion = ConfigurationManager.AppSettings["JsVersion"];
    bool updateEveryAppStart = Convert.ToBoolean(ConfigurationManager.AppSettings["UpdateJsEveryAppStart"]);
    int buildNumber = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.Revision;
    JsPostfix = "";
#if !DEBUG
    JsPostfix += ".min";
#endif      
    JsPostfix += ".js?" + jsVersion + "_" + buildNumber;
    if (updateEveryAppStart)
    {
        Random rand = new Random();
        JsPosfix += "_" + rand.Next();
    }
    ...
}

我正在使用.Ticks(请参阅本页上的答案)
Ravi Ram

5

如今,常见的做法是生成内容哈希码作为文件名的一部分,以强制浏览器(尤其是IE)重新加载javascript文件或css文件。

例如,

供应商。a7561fb0e9a071baadb9 .js
main。b746e3eb72875af2caa9 .js

通常是构建工具(例如webpack)的工作。如果有人想尝试使用webpack,这里有更多详细信息


4

如果要生成链接到JS文件的页面,则一种简单的解决方案是将文件的最后修改时间戳记附加到生成的链接。

这与Huppie的答案非常相似,但是可以在没有关键字替换的版本控制系统中使用。这也比追加当前时间更好,因为即使文件完全没有更改,这样做也可以防止缓存。


我喜欢这种解决方案,因为它最容易维护。如果您更新.js文件,这就是您需要做的。无需更新对文件的任何引用,因为您的代码将自动添加上次更新的时间戳。
NL3294 '16

3

jQuery函数getScript也可以用于确保每次加载页面时确实加载了js文件。

这是我的方法:

$(document).ready(function(){
    $.getScript("../data/playlist.js", function(data, textStatus, jqxhr){
         startProgram();
    });
});

http://api.jquery.com/jQuery.getScript/中检查该功能

默认情况下,$。getScript()将缓存设置设置为false。这会将带时间戳的查询参数附加到请求URL,以确保浏览器在每次请求时都下载脚本。


8
如果没有更改,我们需要缓存文件。
Leo Lee

3

PHP中

function latest_version($file_name){
    echo $file_name."?".filemtime($_SERVER['DOCUMENT_ROOT'] .$file_name);
}

HTML中

<script type="text/javascript" src="<?php latest_version('/a-o/javascript/almanacka.js'); ?>">< /script>

怎么运行的:

在HTML中,filepath您可以像以前一样编写和名称,但只能在函数中。PHP获取filetime文件的并返回filepath+name+"?"+time最新的更改


3

我们一直在为用户创建SaaS,并向他们提供脚本以附加到他们的网站页面上,并且无法附加带有脚本的版本,因为用户会将脚本附加到他们的网站上以实现功能,而我不能强迫他们每次我们更新脚本时都要更改版本

因此,我们找到了一种在用户每次调用原始脚本时加载较新版本的脚本的方法

提供给用户的脚本链接

<script src="https://thesaasdomain.com/somejsfile.js" data-ut="user_token"></script>

脚本文件

if($('script[src^="https://thesaasdomain.com/somejsfile.js?"]').length !== 0) {
   init();
} else {
   loadScript("https://thesaasdomain.com/somejsfile.js?" + guid());
}

var loadscript = function(scriptURL) {
   var head = document.getElementsByTagName('head')[0];
   var script = document.createElement('script');
   script.type = 'text/javascript';
   script.src = scriptURL;
   head.appendChild(script);
}

var guid = function() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
    });
}

var init = function() {
    // our main code
}

说明:

用户已在其网站上附加了提供给他们的脚本,我们检查了脚本附带的唯一令牌是否存在,或者不使用jQuery选择器,如果不存在,则使用较新的令牌(或版本)动态加载该脚本

两次调用同一脚本可能会导致性能问题,但它确实解决了强制将脚本从缓存中加载而不将版本放入提供给用户或客户端的实际脚本链接中的问题。

免责声明:如果性能对于您而言是个大问题,请不要使用。


2

在asp.net mvc中,可以将@ DateTime.UtcNow.ToString()用于js文件版本号。版本号随日期自动更改,您强制客户端浏览器自动刷新js文件。我使用这种方法,并且效果很好。

<script src="~/JsFilePath/JsFile.js?v=@DateTime.UtcNow.ToString()"></script>

与其他建议的解决方案一样,这将导致文件永远不会被缓存,这通常是不希望的。只要未对文件进行任何更改,您可能希望客户端使用缓存的版本,而不是每次都重新下载未更改的文件。
菲利普·斯特拉特福德

出于您的原因,您可以使用以下代码,缓存版本号为<script src =“ ~/JsFilePath/JsFile.js?v=@GetAppVersionNumber()”> </ script>的文件
严重,

2

location.reload(true);

参见https://www.w3schools.com/jsref/met_loc_reload.asp

我动态调用这一行代码,以确保从Web服务器而不是从浏览器的缓存中重新检索了javascript,从而避免了此问题。


onload="location.reload();"表单中添加,使我可以在刷新后获取新的JS,而不必重新启动页面。这是一个更为优雅的解决方案。谢谢!
ZX9

谢谢,可以将其与检查IP是否被识别但自从上次更新后首次登录用户以来在索引页面上未使用过的IP地址一起使用。
Fi Horan

onload =“ location.reload(true);” 上面的代码对我不起作用(使用烧瓶和当前版本的Chrome)也适用于: w3schools.com/jsref/met_loc_reload.asp
aspiringGuru

1

一种解决方案是在获取资源时将带有时间戳的查询字符串附加到URL。这利用了以下事实:浏览器将不会缓存从URL中带有查询字符串的资源。

您可能根本不希望浏览器完全不缓存这些资源。您更可能希望将其缓存,但希望浏览器在文件可用时获取新版本。

最常见的解决方案似乎是在文件名本身中嵌入时间戳或修订号。这需要做更多的工作,因为需要修改您的代码以请求正确的文件,但这意味着,例如,您的版本7 snazzy_javascript_file.js(即snazzy_javascript_file_7.js)会在浏览器中缓存,直到您发布版本8,然后您的代码更改为取snazzy_javascript_file_8.js来代替。


1

使用a而file.js?V=1不是a 的优点fileV1.js是您不需要在服务器上存储JavaScript文件的多个版本。

我遇到的问题file.js?V=1是,在使用新版本的库实用程序时,您可能在另一个JavaScript文件中有依赖代码会中断。

为了向后兼容,我认为最好jQuery.1.3.js在新页面上使用并让现有页面使用jQuery.1.1.js,直到必要时准备升级旧页面为止。


1

使用版本GET变量来防止浏览器缓存。

附加?v=AUTO_INCREMENT_VERSION到网址末尾可防止浏览器缓存-避免任何和所有缓存的脚本。




1

通过标签帮助器在ASP.NET Core中进行缓存清除将为您解决此问题,并允许浏览器保留缓存的脚本/ css,直到文件更改为止。只需将标记助手asp-append-version =“ true”添加到脚本(js)或链接(css)标记中:

<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true"/>

Dave Paquette在此处提供了一个很好的示例以及缓存清除的说明(页面底部)缓存清除


这在常规ASP.NET中不起作用吗?我尝试将asp-append-version添加到我的脚本标签中,所有浏览器看到的都是与源代码中显示的脚本标签完全相同的脚本,包括asp-append-version属性。
tolsen64

这是与标签助手关联的.NET Core属性。它在脚本名称后附加一个版本,以便服务器/浏览器始终看到最新版本并下载
ccherwin

0

最简单的解决方案?不要让浏览器完全缓存。附加当前时间(以毫秒为单位)作为查询。

(您仍处于测试阶段,因此您可以为不针对性能进行优化提供合理的理由。这里是YMMV。)


13
恕我直言,这是一个糟糕的解决方案。如果您不在测试版中并且推出了重要更新怎么办?
d -_- b

0

一种简单的方法。编辑htaccess

RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} \.(jpe?g|bmp|png|gif|css|js|mp3|ogg)$ [NC]
RewriteCond %{QUERY_STRING} !^(.+?&v33|)v=33[^&]*(?:&(.*)|)$ [NC]
RewriteRule ^ %{REQUEST_URI}?v=33 [R=301,L]

这将导致重定向,这在性能上是次优的,但可行的解决方案。
twojr

0

下面为我​​工作:

<head>
<meta charset="UTF-8">
<meta http-equiv="cache-control" content="no-cache, must-revalidate, post-check=0, pre-check=0" />
<meta http-equiv="cache-control" content="max-age=0" />
<meta http-equiv="expires" content="0" />
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
<meta http-equiv="pragma" content="no-cache" />
</head>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.