如何强制浏览器重新加载缓存的CSS/JS文件?

Kip*_*Kip 957 javascript css caching auto-versioning

我注意到一些浏览器(特别是Firefox和Opera)非常热衷于使用.css.js文件的缓存副本,即使在浏览器会话之间也是如此.当您更新其中一个文件但用户的浏览器继续使用缓存副本时,这会导致问题.

问题是:在更改文件时,强制用户浏览器重新加载文件的最优雅方法是什么?

理想情况下,解决方案不会强制浏览器在每次访问页面时重新加载文件.我会发布自己的解决方案作为答案,但我很好奇,如果有人有更好的解决方案,我会让你的投票决定.

更新:

在这里讨论了一段时间后,我发现John Millikinda5id的建议很有用.事实证明,有一个术语:自动版本控制.

我在下面发布了一个新的答案,它是我原来的解决方案和John的建议的组合.

SCdF建议的另一个想法是将伪造的查询字符串附加到文件中.(一些Python代码自动使用时间戳作为伪造的查询字符串由pi提交.).但是,有一些关于浏览器是否会使用查询字符串缓存文件的讨论.(请记住,我们希望浏览器缓存文件并在以后的访问中使用它.我们只希望它在更改后再次获取文件.)

由于不清楚伪造的查询字符串会发生什么,我不接受这个答案.

Kip*_*Kip 446

更新: 重写以纳入John Millikinda5id的建议.此解决方案是用PHP编写的,但应该很容易适应其他语言.

更新2:结合Nick Johnson的评论,原始.htaccess正则表达式可能会导致文件出现问题json-1.3.js.解决方案是仅在末尾恰好有10位数时才重写.(因为10位数字涵盖了从9/9/2001到11/20/2286的所有时间戳.)

首先,我们在.htaccess中使用以下重写规则:

RewriteEngine on
RewriteRule ^(.*)\.[\d]{10}\.(css|js)$ $1.$2 [L]
Run Code Online (Sandbox Code Playgroud)

现在,我们编写以下PHP函数:

/**
 *  Given a file, i.e. /css/base.css, replaces it with a string containing the
 *  file's mtime, i.e. /css/base.1221534296.css.
 *  
 *  @param $file  The file to be loaded.  Must be an absolute path (i.e.
 *                starting with slash).
 */
function auto_version($file)
{
  if(strpos($file, '/') !== 0 || !file_exists($_SERVER['DOCUMENT_ROOT'] . $file))
    return $file;

  $mtime = filemtime($_SERVER['DOCUMENT_ROOT'] . $file);
  return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $file);
}
Run Code Online (Sandbox Code Playgroud)

现在,无论您何时包含CSS,请从以下位置进行更改:

<link rel="stylesheet" href="/css/base.css" type="text/css" />
Run Code Online (Sandbox Code Playgroud)

对此:

<link rel="stylesheet" href="<?php echo auto_version('/css/base.css'); ?>" type="text/css" />
Run Code Online (Sandbox Code Playgroud)

这样,您就不必再次修改链接标记,用户将始终看到最新的CSS.浏览器将能够缓存CSS文件,但是当您对CSS进行任何更改时,浏览器会将此视为新URL,因此它不会使用缓存副本.

这也适用于图像,favicon和JavaScript.基本上任何不动态生成的东西.

  • 我发现这有一个问题,它多次访问文件系统 - 确切地说 - 链接数量*请求数/秒......这对您来说可能是也可能不是问题. (37认同)
  • 我自己的静态内容服务器完全相同,除了我使用参数进行版本控制(base.css?v = 1221534296)而不是文件名更改(base.1221534296.css).我怀疑你的方式可能会更有效率.很酷. (16认同)
  • 第一个`file_exists`检查真的有必要吗?`filemtime`将在失败时返回false,那么为什么不将filemtime值分配给变量并在重命名文件之前检查它是否为false?这将减少一个不必要的文件操作,这将真正加起来. (8认同)
  • @Kip:非常灵活的解决方案.URL改写显然比提供更好的网址提供更多功能. (4认同)
  • @AlixAxel:不,浏览器会在参数更改时重新获取它,但是某些公共代理不会使用url参数缓存文件,因此最佳做法是在路径中包含该版本.与WPO中的每个其他性能瓶颈相比,mod_rewrite开销微乎其微 (3认同)
  • 我将函数修剪为:`$ filetime = strpos($ file,'/')=== 0?filemtime($ _ SERVER ['DOCUMENT_ROOT'].$ file):false; if($ filetime)返回preg_replace('{\\.([^./] +)$}',".$ filetime).\ $ 1",$ file); 否则返回$ file;` (2认同)
  • @TomášFejfar - 当它触及文件系统时可能会出现问题?无论如何,文件系统都会受到很多影响 - 例如数据库。与数据库等其他东西相比,检查文件上次修改时间真的可以很长吗?顺便说一句 - 我在文档中看到 filemtime 的结果被缓存了 http://php.net/manual/en/function.filemtime.php (2认同)

kep*_*aro 181

简单的客户端技术

一般来说,缓存很好..所以有几种技术,取决于你在开发网站时是否自己解决问题,或者你是否试图在生产环境中控制缓存.

您网站的一般访问者将无法获得您在开发网站时所拥有的相同体验.由于普通访问者访问网站的频率较低(可能每月只有几次,除非您是Google或hi5网络),因此他们不太可能将您的文件放在缓存中,这可能就足够了.如果要在浏览器中强制使用新版本,可以随时向请求添加查询字符串,并在进行重大更改时提高版本号:

<script src="/myJavascript.js?version=4"></script>
Run Code Online (Sandbox Code Playgroud)

这将确保每个人都获得新文件.它的工作原理是浏览器查看文件的URL以确定它是否在缓存中有副本.如果您的服务器未设置为对查询字符串执行任何操作,则将忽略该服务器,但该名称将看起来像浏览器的新文件.

另一方面,如果您正在开发网站,则每次保存对开发版本的更改时都不希望更改版本号.那将是乏味的.

因此,在开发网站时,一个好方法是自动生成查询字符串参数:

<!-- Development version: -->
<script>document.write('<script src="/myJavascript.js?dev=' + Math.floor(Math.random() * 100) + '"\><\/script>');</script>
Run Code Online (Sandbox Code Playgroud)

向请求添加查询字符串是对资源进行版本化的好方法,但对于简单的网站,这可能是不必要的.请记住,缓存是一件好事.

值得注意的是,浏览器并不一定吝啬将文件保存在缓存中.浏览器有针对此类事情的策略,它们通常按照HTTP规范中规定的规则进行播放.当浏览器向服务器发出请求时,响应的一部分是EXPIRES标头..一个告诉浏览器应该在缓存中保留多长时间的日期.下次浏览器遇到对同一文件的请求时,它会看到它在缓存中有一个副本,并查看EXPIRES日期以决定是否应该使用它.

不管你信不信,它实际上是你的服务器使浏览器缓存如此持久.您可以调整服务器设置并更改EXPIRES标头,但我上面写的小技巧可能是一种更简单的方法.由于缓存很好,您通常希望将该日期设置为远期("Far-future Expires Header"),并使用上述技术强制进行更改.

如果您对有关HTTP的更多信息或如何提出这些请求感兴趣,那么一本好书就是Steve Souders的"高性能网站".这是对这个主题的一个非常好的介绍.

  • 使用Javascript生成查询字符串的快速技巧在活动开发期间非常有效.我用PHP做了同样的事情. (3认同)
  • 这是完成原始海报所需结果的最简单方法.如果要在加载页面时强制重新加载.css或.js文件,则mod_rewrite方法很有效.此方法仍允许缓存,直到您实际更改文件并真正希望它强制重新加载. (2认同)
  • 当我使用: `&lt;link href='myCss.css?dev=14141'...&gt;` 时,这似乎不适用于我的 CSS (2认同)
  • 这不是可行的解决方案。大量的浏览器只会拒绝缓存带有查询字符串的任何内容。这就是如果您对静态内容的引用具有查询字符串时,Google,GTMetrix和类似工具会引发标记的原因。虽然这肯定是一个不错的开发解决方案,但绝对不是生产解决方案。另外,浏览器控制缓存,而不是服务器。服务器只是建议何时刷新。浏览器不必收听服务器(通常不收听)。移动设备就是一个很好的例子。 (2认同)
  • document.write 解决方案效果太好了,现在我无法在 Chrome 中设置断点,因为 url 不断变化,因此不断刷新并丢失我的断点! (2认同)

Leo*_*opd 111

谷歌的apache mod_pagespeed插件会为你做自动版本控制.这真的很光滑.

它解析HTML从Web服务器出来(使用PHP,rails,python,静态HTML - 任何东西),并重写CSS,JS,图像文件的链接,因此它们包含一个id代码.它在修改后的URL上提供文件,并对它们进行非常长的缓存控制.文件更改后,它会自动更改URL,以便浏览器重新获取它们.它基本上只是工作,没有任何代码更改.它甚至会在出路时缩小你的代码.

  • 当它显然是浏览器问题时,这是错误的(自动摆弄源代码).给我们(开发人员)一个真正的大脑擦除刷新:<ctrl> + F5 (26认同)
  • mod_pagespeed在功能上等同于html/css/js的完全自动构建/编译步骤.我认为你很难找到任何认为构建系统本质上是错误的认真的开发人员,或者认为它是完全自动的任何错误.干净构建的类比是清除mod_pagespeed的缓存:http://code.google.com/p/modpagespeed/wiki/FAQ#How_do_I_clear_the_cache_on_my_server? (25认同)
  • @ T4NK3R mod_pagespeed不需要对你的源做任何事情来进行缓存管理,只是简单地提到它*可以*帮助缩小等内容.至于它是否"错",那完全是主观的.这对你来说可能是错的,但这并不意味着它是绝对的_bad_. (3认同)
  • 它也适用于nginx,但您必须从源代码构建它:https://developers.google.com/speed/pagespeed/module/build_ngx_pagespeed_from_source (2认同)

lev*_*vik 92

我建议您使用实际CSS文件的MD5哈希,而不是手动更改版本.

所以你的网址会是这样的

http://mysite.com/css/[md5_hash_here]/style.css
Run Code Online (Sandbox Code Playgroud)

您仍然可以使用重写规则去除散列,但优点是现在您可以将缓存策略设置为"永久缓存",因为如果URL相同,则表示文件未更改.

然后,您可以编写一个简单的shell脚本来计算文件的哈希并更新您的标记(您可能希望将其移动到单独的文件中以便包含).

每次CSS更改时,只需运行该脚本即可.浏览器只会在更改文件时重新加载.如果您进行编辑然后撤消它,那么确定您需要返回哪个版本以便访问者不要重新下载是没有意义的.

  • 很好的解决方案..但是我认为在每次访问页面时,在每个文件请求(css,js,images,html..etc)中计算文件的哈希值都是很消耗资源的。 (2认同)

Pha*_*007 63

不确定为什么你们为实施这个解决方案付出了太多的痛苦.

如果获取文件的修改时间戳并将其作为查询字符串附加到文件,则需要执行的操作

在PHP中,我会这样做:

<link href="mycss.css?v=<?= filemtime('mycss.css') ?>" rel="stylesheet">
Run Code Online (Sandbox Code Playgroud)

filemtime是一个返回文件修改时间戳的PHP函数.

  • 非常优雅,虽然我稍微将其修改为`<link rel ="stylesheet"href ="mycss.css?<?php echo filemtime('mycss.css')?>"/>`,以防万一关于使用GET变量缓存URL的这个线程的参数(以建议的格式)是正确的 (2认同)

SCd*_*CdF 51

您可以放在?foo=1234css/js导入的末尾,将1234更改为您喜欢的任何内容.看一下SO html源代码示例.

这个想法有吗?无论如何,请求都会丢弃/忽略参数,您可以在推出新版本时更改该数字.


注意:关于它如何影响缓存有一些争论.我相信它的一般要点是有或没有参数的GET请求应该是可缓存的,因此上述解决方案应该可行.

但是,由Web服务器决定是否要遵守规范的那一部分以及用户使用的浏览器,因为它可以直接进入并要求提供新版本.

  • 值得注意的是,无论出于何种原因,Stackoverflow本身都使用查询字符串方法. (33认同)
  • @troelskn:HTTP 1.1规范另有说明(关于带有查询参数的GET和HEAD请求):除非服务器提供明确的过期时间,否则缓存绝不能将对这些URI的响应视为新鲜.见http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.9 (9认同)
  • 我尝试了所有主流浏览器的版本控制查询字符串类型,他们会缓存文件,规格与否.但是,我认为使用style.TIMESTAMP.css格式最好不要滥用查询字符串,因为缓存代理软件仍然可能不会缓存文件. (4认同)
  • 已经验证使用?=参数不会使浏览器在参数更改时重新获取缓存文件.唯一的方法是在服务器端以编程方式更改文件名本身,如Kip所回答 (2认同)

Joh*_*kin 40

我听说这个叫做"自动版本控制".最常见的方法是在URL中的某处包含静态文件的mtime,并使用重写处理程序或URL confs将其删除:

也可以看看:

  • 谢谢,我想这是另一个案例,我的想法已经讨论过,我只是不知道它叫什么,所以我从来没有在谷歌搜索上找到它. (3认同)

Mic*_*pat 24

对于大约2008年的网站,30个左右的现有答案是很好的建议.然而,当涉及到现代的单页面应用程序(SPA)时,可能是时候重新思考一些基本假设了......特别是Web服务器只需要提供单个最新版本的服务的想法文件.

想象一下,您是一个在浏览器中加载了SPA 版本M的用户:

  1. 您的CD管道将应用程序的新版本N部署到服务器上
  2. 您在SPA中导航,它将XHR发送到服务器以获取 /some.template
    • (您的浏览器尚未刷新页面,因此您仍在运行M版本)
  3. 服务器响应内容/some.template- 您是否希望它返回模板的版本MN

如果/some.template版本MN之间的格式发生了变化(或文件被重命名或其他),您可能不希望将模板的版本N发送到运行解析器的旧版本M的浏览器.†

满足两个条件时,Web应用程序会遇到此问题:

  • 在初始页面加载后的某个时间异步请求资源
  • 应用程序逻辑假设有关资源内容的事情(可能在将来的版本中有所改变)

一旦您的应用程序需要并行提供多个版本,解决缓存和"重新加载"变得微不足道:

  1. 安装所有站点文件到版本迪尔斯:/v<release_tag_1>/…files…,/v<release_tag_2>/…files…
  2. 设置HTTP标头以允许浏览器永久缓存文件
    • (或者更好的是,将所有内容都放在CDN中)
  3. 更新所有<script><link>标签等,以指向其中一个版本化目录中的该文件

最后一步听起来很棘手,因为它可能需要为服务器端或客户端代码中的每个URL调用URL构建器.或者你可以巧妙地使用<base>标签并在一个地方更改当前版本.

†解决这个问题的一种方法是积极地强制浏览器在发布新版本时重新加载所有内容.但是为了让任何正在进行的操作完成,可能仍然最容易并行支持至少两个版本:v-current和v-previous.

  • 出色的反应和绝对理想的策略!以及提及 `base` 标签的奖励积分!至于支持旧代码:这并不总是可行的,也不总是一个好主意。新版本的代码可能支持对应用程序其他部分的重大更改,或者可能涉及紧急修复、漏洞补丁等。我自己还没有实现这个策略,但我一直觉得整体架构应该允许部署将旧版本标记为“过时”并在下次进行异步调用时强制重新加载(或者只是强制取消所有通过 WebSockets 的会话)。 (2认同)

小智 14

不要使用foo.css?version = 1!浏览器不应该使用GET变量缓存URL.根据http://www.thinkvitamin.com/features/webapps/serving-javascript-fast,虽然IE和Firefox忽略了这一点,但Opera和Safari却没有!而是使用foo.v1234.css,并使用重写规则去除版本号.

  • 一个Web浏览器,包含缓存对象的功能(检查浏览器的缓存目录).HTTP是一种协议,包括从服务器到客户端(代理,浏览器,蜘蛛等)的指令,建议缓存控制. (13认同)

Nic*_*son 10

RewriteRule需要对js或css文件进行小的更新,这些文件最后包含点符号版本控制.例如json-1.3.js.

我在正则表达式中添加了一个点否定类[^.],所以.number.被忽略了.

RewriteRule ^(.*)\.[^.][\d]+\.(css|js)$ $1.$2 [L]
Run Code Online (Sandbox Code Playgroud)

  • 感谢您的输入!自从我写了这篇文章以来,我也为此而感到困惑。我的解决方案是仅在文件名的最后部分恰好包含十位数字时重写。(10位数字涵盖了从9/9/2001到11/20/2286的所有时间戳。)我已经更新了答案,以包括以下正则表达式:`^(。*)\。[\ d] {10} \。(css | js)$ $ 1. $ 2` (2认同)

use*_*893 10

对于ASP.NET 4.5及更高版本,您可以使用脚本捆绑.

请求http://localhost/MvcBM_time/bundles/AllMyScripts?v=r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81是针对捆绑AllMyScripts并包含查询字符串对v = r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81.查询字符串v具有值标记,该标记是用于高速缓存的唯一标识符.只要捆绑包没有更改,ASP.NET应用程序就会使用此令牌请求AllMyScripts捆绑包.如果包中的任何文件发生更改,ASP.NET优化框架将生成一个新令牌,保证对该包的浏览器请求将获得最新的包.

捆绑还有其他好处,包括首次加载页面时提高性能.


Llo*_*nks 9

这是一个纯JavaScript解决方案

(function(){

    // Match this timestamp with the release of your code
    var lastVersioning = Date.UTC(2014, 11, 20, 2, 15, 10);

    var lastCacheDateTime = localStorage.getItem('lastCacheDatetime');

    if(lastCacheDateTime){
        if(lastVersioning > lastCacheDateTime){
            var reload = true;
        }
    }

    localStorage.setItem('lastCacheDatetime', Date.now());

    if(reload){
        location.reload(true);
    }

})();
Run Code Online (Sandbox Code Playgroud)

以上内容将查找用户上次访问您网站的时间.如果上次访问是在您发布新代码之前,则使用它location.reload(true)来强制从服务器刷新页面.

我通常将此作为第一个脚本,<head>因此在任何其他内容加载之前进行评估.如果需要重新加载,则用户几乎不会注意到.

我使用本地存储将最后一次访问时间戳存储在浏览器上,但是如果您希望支持旧版本的IE,则可以添加cookie.


Kam*_*ski 9

在Laravel(PHP)中,我们可以按照清晰优雅的方式(使用文件修改时间戳)来完成:

<script src="{{ asset('/js/your.js?v='.filemtime('js/your.js')) }}"></script>
Run Code Online (Sandbox Code Playgroud)

和CSS类似

<link rel="stylesheet" href="{{asset('css/your.css?v='.filemtime('css/your.css'))}}">
Run Code Online (Sandbox Code Playgroud)


Mic*_*iel 8

有趣的帖子.阅读完这里的所有答案,结合我从未遇到过"伪造"查询字符串的任何问题(我不确定为什么每个人都不愿意使用这个)我猜这个解决方案(不需要apache重写规则)如在接受的答案中)是计算CSS文件内容的短HASH(而不是文件日期时间)作为伪造的查询字符串.

这将导致以下结果:

<link rel="stylesheet" href="/css/base.css?[hash-here]" type="text/css" />
Run Code Online (Sandbox Code Playgroud)

当然,日期时间解决方案也可以在编辑CSS文件的情况下完成工作,但我认为它是关于css文件内容而不是文件日期时间,那么为什么要将这些混合起来?


Fra*_*yce 7

对于我的开发,我发现chrome有很好的解决方案。

https://developer.chrome.com/devtools/docs/tips-and-tricks#hard-reload

在开发人员工具打开的情况下,只需长按刷新按钮,然后将鼠标悬停在“空缓存和硬重载”上就可以释放。

这是我最好的朋友,并且是获得您想要的东西的超轻量级方法!


pi.*_*pi. 6

我最近使用 Python 解决了这个问题。这是代码(它应该很容易被其他语言采用):

def import_tag(pattern, name, **kw):
    if name[0] == "/":
        name = name[1:]
    # Additional HTML attributes
    attrs = ' '.join(['%s="%s"' % item for item in kw.items()])
    try:
        # Get the files modification time
        mtime = os.stat(os.path.join('/documentroot', name)).st_mtime
        include = "%s?%d" % (name, mtime)
        # This is the same as sprintf(pattern, attrs, include) in other
        # languages
        return pattern % (attrs, include)
    except:
        # In case of error return the include without the added query
        # parameter.
        return pattern % (attrs, name)

def script(name, **kw):
    return import_tag('<script %s src="/%s"></script>', name, **kw)

def stylesheet(name, **kw):
    return import_tag('<link rel="stylesheet" type="text/css" %s href="/%s">', name, **kw)
Run Code Online (Sandbox Code Playgroud)

此代码基本上将文件时间戳作为查询参数附加到 URL。下面函数的调用

script("/main.css")
Run Code Online (Sandbox Code Playgroud)

会导致

<link rel="stylesheet" type="text/css"  href="/main.css?1221842734">
Run Code Online (Sandbox Code Playgroud)

好处当然是你永远不必再次更改你的 HTML 内容,触摸 CSS 文件会自动触发缓存失效。它工作得很好,开销并不明显。

  • @LarryBud:这是文件的时间戳,而不是当前时间戳。你肯定会有缓存。 (2认同)

lon*_*ony 6

感谢Kip的完美解决方案!

我扩展它以将其用作Zend_view_Helper.因为我的客户端在虚拟主机上运行他的页面,我也为此扩展了它.

希望它也可以帮助其他人.

/**
 * Extend filepath with timestamp to force browser to
 * automatically refresh them if they are updated
 *
 * This is based on Kip's version, but now
 * also works on virtual hosts
 * @link http://stackoverflow.com/questions/118884/what-is-an-elegant-way-to-force-browsers-to-reload-cached-css-js-files
 *
 * Usage:
 * - extend your .htaccess file with
 * # Route for My_View_Helper_AutoRefreshRewriter
 * # which extends files with there timestamp so if these
 * # are updated a automatic refresh should occur
 * # RewriteRule ^(.*)\.[^.][\d]+\.(css|js)$ $1.$2 [L]
 * - then use it in your view script like
 * $this->headLink()->appendStylesheet( $this->autoRefreshRewriter($this->cssPath . 'default.css'));
 *
 */
class My_View_Helper_AutoRefreshRewriter extends Zend_View_Helper_Abstract {

    public function autoRefreshRewriter($filePath) {

        if (strpos($filePath, '/') !== 0) {

            // path has no leading '/'
            return $filePath;
        } elseif (file_exists($_SERVER['DOCUMENT_ROOT'] . $filePath)) {

            // file exists under normal path
            // so build path based on this
            $mtime = filemtime($_SERVER['DOCUMENT_ROOT'] . $filePath);
            return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $filePath);
        } else {

            // fetch directory of index.php file (file from all others are included)
            // and get only the directory
            $indexFilePath = dirname(current(get_included_files()));

            // check if file exist relativ to index file
            if (file_exists($indexFilePath . $filePath)) {

                // get timestamp based on this relativ path
                $mtime = filemtime($indexFilePath . $filePath);

                // write generated timestamp to path
                // but use old path not the relativ one
                return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $filePath);
            } else {

                return $filePath;
            }
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

干杯谢谢.


小智 6

尚未找到客户端DOM方法动态创建脚本节点(或css)元素:

<script>
    var node = document.createElement("script"); 
    node.type = "text/javascript";
    node.src = 'test.js?'+Math.floor(Math.random()*999999999);
    document.getElementsByTagName("head")[0].appendChild(node);
</script>
Run Code Online (Sandbox Code Playgroud)


unk*_*123 6

谷歌浏览器具有“ 硬重装 ”以及“ 清空缓存和硬重装”选项。您可以单击并按住“重装”按钮(在检查模式下)以选择一个。


hel*_*ios 5

如果将session-id添加为js/css文件的spureous参数,则可以强制执行"会话范围的缓存":

<link rel="stylesheet" src="myStyles.css?ABCDEF12345sessionID" />
<script language="javascript" src="myCode.js?ABCDEF12345sessionID"></script>
Run Code Online (Sandbox Code Playgroud)

如果您想要版本范围的缓存,可以添加一些代码来打印文件日期或类似内容.如果您使用的是Java,则可以使用自定义标记以优雅的方式生成链接.

<link rel="stylesheet" src="myStyles.css?20080922_1020" />
<script language="javascript" src="myCode.js?20080922_1120"></script>
Run Code Online (Sandbox Code Playgroud)


Wal*_*sby 5

假设您有一个文件可用于:

/styles/screen.css
Run Code Online (Sandbox Code Playgroud)

您可以将带有版本信息的查询参数附加到URI上,例如:

/styles/screen.css?v=1234
Run Code Online (Sandbox Code Playgroud)

或者您可以添加版本信息,例如:

/v/1234/styles/screen.css
Run Code Online (Sandbox Code Playgroud)

恕我直言,第二种方法更适合CSS文件,因为它们可以使用相对URL引用图像,这意味着如果你指定background-image如此:

body {
    background-image: url('images/happy.gif');
}
Run Code Online (Sandbox Code Playgroud)

它的URL实际上是:

/v/1234/styles/images/happy.gif
Run Code Online (Sandbox Code Playgroud)

这意味着如果更新使用的版本号,服务器会将其视为新资源,而不是使用缓存版本.如果您的版本号基于Subversion/CVS/etc.修订这意味着将注意到对CSS文件中引用的图像的更改.不与第一方案保证,即URL images/happy.gif相对/styles/screen.css?v=1235/styles/images/happy.gif不包含任何版本信息.

我已经使用Java servlet使用这种技术实现了一个缓存解决方案,并简单地处理/v/*委托给底层资源(即/styles/screen.css)的servlet的请求.在开发模式中,我设置了缓存标头,告诉客户端始终使用服务器检查资源的新鲜度(在部署模式下,如果您委托给Tomcat,DefaultServlet并且等文件没有更改,这通常会导致304 )我设置标题"永远缓存"..css.js

  • 使用第二种方法,对 CSS 的更改将使所有使用相对 URL 引用的图像的缓存副本无效,这可能是可取的,也可能是不可取的。 (2认同)

Pon*_* VN 5

你可以简单地用CSS/JS url添加一些随机数

example.css?randomNo=Math.random()
Run Code Online (Sandbox Code Playgroud)


Iva*_*kin 5

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

通过这种方式包含的Js或Css文件:

<script type="text/javascript" src="Scripts/exampleScript<%=Global.JsPostfix%>" />
<link rel="stylesheet" type="text/css" href="Css/exampleCss<%=Global.CssPostfix%>" />
Run Code Online (Sandbox Code Playgroud)

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();
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)


rea*_*kus 5

如果您使用Git和 PHP,您可以在每次 Git 存储库发生更改时从缓存中重新加载脚本,使用以下代码:

exec('git rev-parse --verify HEAD 2> /dev/null', $gitLog);
echo '  <script src="/path/to/script.js"?v='.$gitLog[0].'></script>'.PHP_EOL;
Run Code Online (Sandbox Code Playgroud)


小智 5

只需在您想要执行硬重新加载的位置添加此代码(强制浏览器重新加载缓存的 CSS 和 JavaScript 文件):

$(window).load(function() {
    location.reload(true);
});
Run Code Online (Sandbox Code Playgroud)

在 内执行此操作.load,这样它就不会像循环一样刷新。


ast*_*ght 5

对于开发:使用浏览器设置:例如,Chrome network tab有一个disable cache选项。

对于生产:使用服务器端渲染框架或纯 JavaScript 代码将唯一的查询参数附加到请求(例如q?Date.now())。

// Pure JavaScript unique query parameter generation
//
//=== myfile.js

function hello() { console.log('hello') };

//=== end of file

<script type="text/javascript">
    document.write('<script type="text/javascript" src="myfile.js?q=' + Date.now() + '">
    // document.write is considered bad practice!
    // We can't use hello() yet
</script>')

<script type="text/javascript">
    hello();
</script>
Run Code Online (Sandbox Code Playgroud)