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

时间:2008-08-28 14:30:26

标签: javascript caching versioning

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

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

我确信我们不是第一个处理这个问题的人,我想我会把它扔给社区。在更新代码时,如何确保客户更新缓存?如果您使用上述方法,您是否正在使用简化更改的流程?

28 个答案:

答案 0 :(得分:490)

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

例如:

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

  

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

您可能有适合您的版本控制系统吗?大多数版本控制系统都有办法在登记时自动注入修订号。

它看起来像这样:

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

当然,总有更好的解决方案,例如this one

答案 1 :(得分:82)

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

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

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

对于测试版,如果您想强制用户始终获取最新信息,您可以使用:

Cache-Control: no-cache, must-revalidate

答案 2 :(得分:42)

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

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

答案 3 :(得分:32)

  

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

这个答案迟了6年,但我在很多地方都没有看到这个答案...... HTML5引入了Application Cache来解决这个问题。我发现我写的新服务器代码正在崩溃存储在人们浏览器中的旧javascript,所以我想找到一种方法来使他们的javascript过期。使用如下所示的清单文件:

CACHE MANIFEST
# Aug 14, 2014
/mycode.js

NETWORK:
*

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

答案 4 :(得分:25)

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

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

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

当您更新文件并且更新结果的文件大小相同时,该怎么办?赔率是多少?

答案 5 :(得分:18)

并非所有浏览器都会在其中缓存'?'文件。我做了什么来确保它尽可能地被缓存,我在文件名中包含了该版本。

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

我使用mod_redirect(我认为)在apache中have stuff_*.jsstuff.js

答案 6 :(得分:11)

对于ASP.NET页面,我使用以下

<强> BEFORE

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

AFTER(强制重新加载)

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

添加DateTime.Now.Ticks可以很好地工作。

答案 7 :(得分:6)

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

答案 8 :(得分:4)

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

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

答案 9 :(得分: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选择器检查附加脚本的唯一令牌是否存在,如果没有,则使用更新的令牌(或版本)动态加载它

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

  

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

答案 10 :(得分:2)

location.reload(true);

请参阅https://www.w3schools.com/jsref/met_loc_reload.asp

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

答案 11 :(得分:2)

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

我就这样做了:

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

检查http://api.jquery.com/jQuery.getScript/

处的功能

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

答案 12 :(得分:1)

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

答案 13 :(得分:1)

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

例如

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

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

答案 14 :(得分:1)

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

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

Dave Paquette有一个很好的示例和解释缓存清除(页面底部)Cache Busting

答案 15 :(得分:1)

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

答案 16 :(得分:1)

虽然它是特定于框架的,但Django 1.4有this functionailty,其工作方式与above answer

中“greenfelt”网站的链接类似。

答案 17 :(得分:1)

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

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

答案 18 :(得分:1)

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

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

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

答案 19 :(得分:0)

一个简单的方法。 编辑htaccess

reloadable="true"

答案 20 :(得分:0)

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

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

为了向后兼容,我认为将jQuery.1.3.js用于新页面并让现有页面使用jQuery.1.1.js要好得多,直到您准备好升级旧页面为止,如有必要

答案 21 :(得分:0)

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

(你还处于测试阶段,所以你可以为不优化性能做出合理的判断。但YMMV在这里。)

答案 22 :(得分:0)

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

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

答案 23 :(得分: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>

答案 24 :(得分:0)

您可以在文件名中添加文件版本,如下所示:

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

答案 25 :(得分:0)

前端选项

我专门针对无法更改后端任何设置的用户设置了此代码 。在这种情况下,防止超长缓存的最佳方法是:

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

答案 26 :(得分:0)

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

在页面顶部添加类似

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

看起来像

enter image description here

让这行javascript成为加载页面时最后执行的代码:

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

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

enter image description here

答案 27 :(得分:-1)

如果您使用 PHP 和 Javascript,那么以下内容应该对您有用,尤其是在您对文件进行多次更改的情况下。所以,每次你都不能改变它的版本。所以,想法是在PHP中创建一个随机数,然后将其分配为JS文件的一个版本。

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