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

我们目前正在进行私人测试版,因此仍在进行相当快速的更改,尽管显然随着使用率开始增加,我们将放慢这个过程。话虽如此,我们遇到的一个问题是,在我们推出带有新JavaScript文件的更新后,客户端浏览器仍然使用文件的缓存版本,他们看不到更新。显然,在支持电话中,我们可以简单地通知他们进行ctrlF5刷新,以确保他们从服务器获得最新的文件,但最好在那之前处理这件事。

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

我相信我们不是第一个处理这个问题的人,所以我想我会把它扔给社区。当你更新代码时,你如何确保客户端更新他们的缓存?如果你使用的是上述方法,你是否使用了简化更改的过程?

586276 次浏览

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

例如:

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

我假设在这一点上,没有比find-替换更好的方法来在所有脚本标签中增加这些“版本号”吗?

您可能有一个版本管理系统为您做到这一点?例如,大多数版本控制系统都有一种在签入时自动注入修订号的方法。

它看起来像这样:

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

当然,总有更好的解决方案,比如这一个

我的同事刚刚在我在http://www.stefanhayden.com/blog/2006/04/03/css-caching-hack/上发布(参考css)之后找到了对该方法的引用。很高兴看到其他人正在使用它并且它似乎有效。我认为在这一点上,没有比find-替换更好的方法来在所有脚本标签中增加这些“版本号”吗?

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

不过,您可能根本不希望浏览器不缓存这些资源;您更有可能希望缓存它们,但您希望浏览器在文件可用时获取新版本。

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

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

(您仍处于测试阶段,因此您可以合理地说明不优化性能。但是YMMV在这里。)

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

这与Huppie的回答非常相似,但适用于没有关键字替换的版本控制系统。它也比追加当前时间更好,因为即使文件根本没有更改,这也会阻止缓存。

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

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

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

对于beta,如果您想强制用户始终获取最新消息,您可以使用:

Cache-Control: no-cache, must-revalidate

并非所有浏览器都缓存包含“?”的文件。我所做的是确保尽可能缓存它,我将版本包含在文件名中。

所以我没有stuff.js?123,而是stuff_123.js

我在apache中使用mod_redirect(我认为)tohave stuff_*.js to gostuff.js

fileV1.js相比,使用file.js?V=1的优势在于您不需要在服务器上存储多个版本的JavaScript文件。

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

为了向后兼容,我认为最好对新页面使用jQuery.1.3.js,并让现有页面使用jQuery.1.1.js,直到您准备好升级旧页面(如有必要)。

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

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

Google Page-Speed:不要在静态资源的URL中包含查询字符串。 大多数代理,尤其是3.0版的Squid,即使响应中存在Cache-control: public标头,也不会在其URL中缓存带有“?”的资源。要为这些资源启用代理缓存,请从对静态资源的引用中删除查询字符串,而是将参数编码到文件名本身。

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

尽管它是特定于框架的,但Django 1.4具有静态文件应用程序功能,其工作方式与上述答案中的“绿色感觉”站点类似

php中:

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

超文本标记语言中:

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

它是如何工作的:

在超文本标记语言中,编写filepath和名称,但仅限在函数中。 PHP获取文件的filetime并返回最新更改的filepath+name+"?"+time

对于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();
}
...
}

对于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效果非常好。

此用法已被弃用: 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:
*

并在每次您希望用户更新其缓存时生成带有新时间戳的此文件。作为补充说明,如果您添加此项,浏览器将没有重新加载(即使用户刷新页面),直到清单告诉它。

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

我是这样做的:

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

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

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

如何添加文件大小作为加载参数?

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

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

当你更新文件时,你的更新结果是相同的文件大小?几率是多少?

一个简单的方法。 编辑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]

通过标记助手在ASP.NETCore中缓存破坏将为您处理此问题,并允许您的浏览器保留缓存的脚本/css,直到文件更改。只需将标记助手asp-append-version="true"添加到您的脚本(js)或链接(css)标签:

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

戴夫·帕奎特有一个很好的例子和解释缓存破坏这里(页面底部)缓存破坏

我们一直在为用户创建一个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
}

说明:

用户在他们的网站上附加了提供给他们的脚本,我们检查了脚本附加的唯一令牌是否存在,如果没有,则使用较新的令牌(或版本)动态加载

这是两次调用相同的脚本,这可能是一个性能问题,但它真正解决了强制脚本不从缓存加载的问题,而不会将版本放在给用户或客户端的实际脚本链接中

免责声明:如果性能在您的情况下是一个大问题,请不要使用。

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

例如,

供应商。a7561fb0e9a071baadb9.js
EYZ0.js

它通常是webpack等构建工具的工作。如果有人想尝试使用webpack,这里有更多细节

在asp.netmvc中,您可以使用@日期时间。Utc Now。to String()作为js文件版本号。版本号随日期自动更改,您强制客户端浏览器自动刷新js文件。我使用这种方法,效果很好。

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

下面为我工作:

<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>

location.reload(true)

查看https://www.w3schools.com/jsref/met_loc_reload.asp

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

您可以将文件版本添加到您的文件名,因此它将类似于:

https://www.example.com/script_fv25.js

fv25=>文件版本nr。25

并在您的. htaccess中放置此块,该块将从链接中删除版本部分:

RewriteEngine On
RewriteRule (.*)_fv\d+\.(js|css|txt|jpe?g|png|svg|ico|gif) $1.$2 [L]

最后的链接将是:

https://www.example.com/script.js

前端选项

我为那些无法更改后端任何设置的人制作了这段代码特别是。在这种情况下,防止缓存过长的最佳方法是:

new Date().getTime()

然而,对于大多数程序员来说,缓存可能是几分钟或几个小时,所以上面的简单代码最终迫使所有用户下载“浏览的每个页面”。为了指定此项在不重新加载的情况下保留多长时间,我做了此代码并在下面留下了几个示例:

// cache-expires-after.js v1
function cacheExpiresAfter(delay = 1, prefix = '', suffix = '') { // seconds
let now = new Date().getTime().toString();
now = now.substring(now.length - 11, 10); // remove decades and milliseconds
now = parseInt(now / delay).toString();
return prefix + now + suffix;
};


// examples (of the delay argument):
// the value changes every 1 second
var cache = cacheExpiresAfter(1);
// see the sync
setInterval(function(){
console.log(cacheExpiresAfter(1), new Date().getSeconds() + 's');
}, 1000);


// the value changes every 1 minute
var cache = cacheExpiresAfter(60);
// see the sync
setInterval(function(){
console.log(cacheExpiresAfter(60), new Date().getMinutes() + 'm:' + new Date().getSeconds() + 's');
}, 1000);


// the value changes every 5 minutes
var cache = cacheExpiresAfter(60 * 5); // OR 300


// the value changes every 1 hour
var cache = cacheExpiresAfter(60 * 60); // OR 3600


// the value changes every 3 hours
var cache = cacheExpiresAfter(60 * 60 * 3); // OR 10800


// the value changes every 1 day
var cache = cacheExpiresAfter(60 * 60 * 24); // OR 86400


// usage example:
let head = document.head || document.getElementsByTagName('head')[0];
let script = document.createElement('script');
script.setAttribute('src', '//unpkg.com/sweetalert@2.1.2/dist/sweetalert.min.js' + cacheExpiresAfter(60 * 5, '?'));
head.append(script);


// this works?
let waitSwal = setInterval(function() {
if (window.swal) {
clearInterval(waitSwal);
swal('Script successfully injected', script.outerHTML);
};
}, 100);

一个简单的技巧,可以很好地防止旧的和新的javascript文件之间的冲突。这意味着:如果有冲突并且发生一些错误,将提示用户按Ctrl-F5。

在页面的顶部添加类似的内容

<h1 id="welcome"> Welcome to this page <span style="color:red">... press Ctrl-F5</span></h1>

看起来像

输入图片描述

让这行javascript在加载页面时最后执行:

document.getElementById("welcome").innerHTML = "Welcome to this page"

如果没有错误发生,上面的欢迎问候语几乎不可见,几乎立即被替换为

输入图片描述

如果您使用的是PHP和Javascript,那么以下操作应该适用于您,特别是在您对文件进行多次更改的情况下。因此,每次您都无法更改其版本。因此,我们的想法是在PHP中创建一个随机数,然后将其分配为JS文件的版本。

$fileVersion = rand();
<script src="addNewStudent.js?v=<?php echo $fileVersion; ?>"></script>

您可以使用. htaccess执行此操作

将以下行添加到您的. htaccess文件中:

# DISABLE CACHING
<IfModule mod_headers.c>
<FilesMatch "\.js$">
Header set Cache-Control "no-store, max-age=0"
</FilesMatch>
</IfModule>
<script>
var version = new Date().getTime();
var script = document.createElement("script");
script.src = "app.js?=" + version;
document.body.appendChild(script);
</script>

如果有人已经在上面的大量答案中发布了它,请随意删除它。