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

Posted

技术标签:

【中文标题】如何强制客户端刷新 JavaScript 文件?【英文标题】:How can I force clients to refresh JavaScript files? 【发布时间】:2010-09-07 03:21:59 【问题描述】:

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

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

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

【问题讨论】:

***.com/questions/118884/… 【参考方案1】:

前端选项

我为那些无法在后端更改任何设置的人专门编写了此代码。在这种情况下,防止缓存过长的最佳方法是:

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

【讨论】:

【参考方案2】:

虽然它是特定于框架的,但 Django 1.4 有 the staticfiles app functionality,其工作方式与 above answer 中的“greenfelt”站点类似

【讨论】:

【参考方案3】:

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

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

【讨论】:

【参考方案4】:

一个简单的技巧对我来说很好,可以防止新旧 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"

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

【讨论】:

哦不,请不要这样做【参考方案5】:

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

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

【讨论】:

【参考方案6】:

location.reload(true);

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

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

【讨论】:

onload="location.reload();" 添加到我的表单允许我在刷新后获取新的JS,而不是重新启动我的页面。这是一个更优雅的解决方案。谢谢! 谢谢,可以使用它来检查 ip 是否被识别但自上次更新以来尚未用于登录用户初始登录后在索引页面上执行此操作。 onload="location.reload(true);"以上对我也不起作用(使用烧瓶和当前版本的 Chrome):w3schools.com/jsref/met_loc_reload.asp【参考方案7】:

以下对我有用:

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

【讨论】:

【参考方案8】:

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

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

【讨论】:

与其他建议的解决方案一样,这将导致文件永远不会被缓存,这通常是不可取的。只要没有对文件进行任何更改,您可能希望客户端使用缓存版本,而不是每次都重新下载未更改的文件。 你可以使用下面的代码,缓存版本号为 【参考方案9】:

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

并在每次您希望用户更新其缓存时生成带有新时间戳的文件。附带说明一下,如果您添加它,浏览器将不会重新加载(即使用户刷新页面),直到清单告诉它这样做。

【讨论】:

这个方案真的很好,只要你记得更新manifest文件就可以了:) 请阅读文档,因为此功能已从 Web 标准中删除 developer.mozilla.org/en-US/docs/Web/HTML/… FWIW,我最终没有使用这个解决方案。使用/维护?&lt;version&gt; 方法要容易得多。 现在已弃用!【参考方案10】:

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

例如,

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

这通常是 webpack 等构建工具的工作。如果您正在使用 webpack,如果有人想尝试一下,这里还有更多 details。

【讨论】:

【参考方案11】:

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

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

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

【讨论】:

您能否详细说明您在 .htaccess 中使用 mod_redirect 做了什么? 这个方法的详细解释可以看particletree.com/notebook/… 如果您可以在答案中包含.htaccess 代码以供将来参考,那就太好了。 哪些浏览器不缓存带有“?”的文件在里面?【参考方案12】:

我们一直在为用户创建一个 SaaS 并为他们提供一个脚本以附加在他们的网站页面中,并且无法附加带有脚本的版本,因为用户会将脚本附加到他们的网站以获得功能,我可以' t 每次我们更新脚本时都强制他们更改版本

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

提供给用户的脚本链接

<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 选择器检查了脚本附带的唯一令牌是否存在,如果不存在,则使用更新的令牌(或版本)动态加载它

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

免责声明:如果性能对您来说是个大问题,请勿使用。

【讨论】:

【参考方案13】:

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

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

Dave Paquette 在此处(页面底部)有一个很好的缓存破坏示例和解释Cache Busting

【讨论】:

这在常规 ASP.NET 中不起作用吗?我尝试将 asp-append-version 添加到我的脚本标签中,浏览器看到的只是脚本标签,与它在源代码中的显示完全相同,包括 asp-append-version 属性。 这是一个与 Tag Helpers 关联的 .NET Core 属性。它将脚本名称附加一个版本,以便服务器/浏览器始终看到最新版本并下载【参考方案14】:

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

【讨论】:

这会导致重定向,这在性能方面是次优但有效的解决方案。【参考方案15】:

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

添加文件大小作为加载参数怎么样?

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

因此,每次更新文件时,“filever”参数都会发生变化。

当您更新文件并且您的更新导致相同的文件大小时怎么样?几率是多少?

【讨论】:

这个使用PHP标签,如果使用PHP,确实是个好主意。 我认为添加更改日期会比文件大小更好:) 我最初的想法是添加文件的哈希而不是版本。 我认为如果添加时间戳 Unix 也可以工作,对吧?例如'...file.js?filever==time()?> 使用 filemtime($file) 它输出文件的时间戳,使用 time() 你不能使用缓存,因为它每秒都在变化。【参考方案17】:

jQuery函数getScript也可以用来保证每次页面加载时确实加载一个js文件。

我就是这样做的:

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

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

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

【讨论】:

如果没有变化,我们需要缓存文件。【参考方案18】:

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

例如:

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

我现在假设没有比 find-replace 更好的方法来增加所有脚本标签中的这些“版本号”?

您可能有一个版本控制系统来为您做这件事?例如,大多数版本控制系统都有办法在签入时自动注入修订号。

看起来像这样:

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

当然,总是有更好的解决方案,比如this one。

【讨论】:

有谁知道IE7是否忽略了这一点?当我在 IE8 可比性视图中测试时,它似乎忽略了附加数据并使用缓存文件。 我一直都知道查询字符串是 ?ver=123 中的键值对。谢谢! :) 我认为这与更高或更低的版本号无关,而是关于将附加变量值更改为浏览器尚未缓存的值。 提醒:这被认为是一种黑客行为。此方法诱使浏览器认为正在指定一个新文件,因为它只是查看完整的文件名而不解释它。 foo.js?1foo.js?2 不是同名的,所以浏览器会认为它们是两个不同的文件。一个缺点是这两个文件将同时存在于用户的缓存中,占用了不必要的空间。 @LeeWhite 无论您如何处理问题,这两个文件都将缓存在浏览器中。要么是因为它们具有不同的请求参数或不同的路径。所以我认为这不是请求参数方法的缺点。【参考方案19】:

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

【讨论】:

这违背了客户端所有的缓存机制。虚拟参数应替换为“major version_minor_version_build_number_Revision”之类的内容,每个版本都是唯一的。 虽然这在开发环境中可能是一个很好的解决方案,但它不适合生产。这将完全禁用文件的缓存每次加载页面时。想象一下,一个 50Kb 的文件每天加载 10k 页面,它代表每天 500Mb 的 Javascript 文件。 @PhilDulac 您可以将其从 Ticks 更改为返回例如一天、一个月或一周的字符串值。最终它只是向您展示如何使用 ?v 方法 @alex 确实。我只是想警告说,如果答案中展示的用法进入生产环境,它可能会产生开发中未显示的影响。 确保每天加载一次新副本的一种可能方法是使用 ''。所以它在一天开始时加载一次,然后缓存。【参考方案20】:

对于 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(请参阅此页面上的答案)【参考方案21】:

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

【讨论】:

这似乎适用于 .css 和 .js 文件。【参考方案22】:

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

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

【讨论】:

我试过了?解决方案,在 IE8 中,我得到一个 javascript 错误。 Mod重写是一种选择,但在大多数情况下,我们不会对服务器进行太多控制。我更喜欢在 js 文件本身中附加版本或为每个版本都有一个文件夹 @Hắc Huyền Minh:但是当要重新加载脚本时,它不应该从代理缓存重新加载...【参考方案23】:

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

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

【讨论】:

【参考方案24】:

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

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

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

【讨论】:

【参考方案25】:

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

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

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

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

Cache-Control: no-cache, must-revalidate

【讨论】:

你能说得具体点吗? 他在谈论网络服务器为每个文件发送的标头。例如,应该可以在 Apache 中进行配置。我认为这将是最好的方法 你在哪里配置这个? 对于开发 webapp,它可能是一个很好的解决方案。对于您不想让缓存永远失效的生产站点,除非您知道每个目标客户端浏览器都已访问该站点,否则这不是一个好的解决方案。它让我想到了一个潜在的 Web 服务器功能:根据配置的部署日期调整 max-age 参数。那太棒了。 Chrome 需要这些设置才能正确缓存。没有它们,Chrome 将永远缓存文件。 Mozilla 使用更合理的默认值。查看更多信息:agiletribe.wordpress.com/2018/01/29/caching-for-chrome【参考方案26】:

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

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

【讨论】:

我喜欢这个解决方案,因为它最容易维护。如果您更新 .js 文件,这就是您需要做的所有事情。无需更新对文件的任何引用,因为您的代码将自动添加最后更新的时间戳。【参考方案27】:

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

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

【讨论】:

恕我直言,这是一个糟糕的解决方案。如果您不在 BETA 中并推出重要更新怎么办?【参考方案28】:

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

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

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

【讨论】:

以上是关于如何强制客户端刷新 JavaScript 文件?的主要内容,如果未能解决你的问题,请参考以下文章

我可以使用 JavaScript 在 iframe 上强制刷新吗?

如何强制 MyEclipse 将 JavaScript 文件热部署到我的 JBoss 实例?

我可以使用 javascript 强制浏览器“刷新”任何待处理的布局更改吗?

如何告诉 OkHttpClient 忽略缓存并从服务器强制刷新?

JavaScript 获取发布请求后如何防止页面刷新?

无法在 javascript 中强制刷新画布