当您开发网站时,会进行大量的“刷新”操作。 您开始对浏览器在单次刷新中会获取什么和不会获取什么有了一个很好的了解。 例如,我发现,如果我在服务器上覆盖了一个图像文件,则需要 **刷新两次** 该图像才能在实时网站上更新。 然后,我可能会切换到 Opera 并查看该网站在那里的运行情况,结果发现页面第一次渲染时是 **非常旧的** 版本。 糟糕。 刷新。 刷新。 哦... 出现了。
这对您来说没什么问题,您已经习惯了这种事情。 但是,如果您将相同的链接发送给您的客户,并且发生了同样的事情怎么办。 很有可能(至少)会造成一些混乱。 在更大范围内,假设您在一个流量很大的网站上推出了一次相当重大的布局变更。 由于 CSS 缓存,许多人可能在下一次访问时看到布局混乱。
我们能解决这个问题吗? 是否有办法阻止 CSS 缓存?
为您的 CSS 添加时间戳
有一个小技巧,我相信它来自 JavaScript 程序员的世界。 为了阻止他们的脚本缓存,他们在 src
属性的末尾添加了一个时间戳。 因此,让我们窃取这个想法。
我做了一个 快速测试,看看这种胡闹是否真的可行。 以下是我如何包含样式表链接
<link rel="stylesheet" type="text/css" href="style.css?<?php echo date('l jS \of F Y h:i:s A'); ?>" />
结果是
<link rel="stylesheet" type="text/css" href="style.css?Thursday 24th of April 2008 04:45:21 PM" />
根据需要更改 日期格式。 您可能想要跳过空格。
这里理论是,该链接每秒都会更改,浏览器会被欺骗以为这是一个新的样式表,并每次都会重新加载它。 Jason Edmond Beaird 有 相同的想法,甚至创建了一个小书签来强制执行它。
我还没有对此进行任何深入测试,但我的早期测试表明它运行良好。 您对此怎么看? 我只是在喝“酷饮”吗? 这样做有什么严重的问题吗? 是否有更好/更智能/更快的方法?
更新
我在这里更新了 2013 年 6 月的信息,只是要指出一些重要的事情。
在生产环境中,在推出新 CSS 时破坏缓存是一个非常好的计划。 但是,使用这里介绍的日期戳破坏缓存意味着您将无法从浏览器缓存中获得任何好处,这将对性能造成灾难性影响。 您最好使用其他方法,例如在文件名末尾添加版本号,例如 style.css?version=3.2
,然后根据需要更新它。
在本地开发环境中,可能可以接受在每次页面加载时破坏缓存,但是现代开发工具(如 Chrome 的 Web 检查器)可以关闭缓存。
您可以在 Apache 中加载 Expires 模块,然后添加以下指令
ExpiresActive On
ExpiresDefault “access plus 10 days”
ExpiresByType text/css “access plus 1 second”
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault “access plus 10 days”
ExpiresByType text/css “access plus 1 second”
</IfModule>
我知道这种方法只适用于开发使用。 我想不出任何理由在保持基本相同的普通网站上使用它。 如果我错了请纠正我。
我相信 Rails 会自动执行此操作(当您在视图模板中使用正确的标签时)。
此外,如果您使用的是 Subversion 或其他版本控制系统,则可以将版本信息添加到 CSS 文件的末尾。 这样,只有在版本更改时才会刷新,而不是每次刷新。
这是在 YSlow 中获得 F 等级的最佳方法 :-)
Particletree 在几个月前发表了一篇关于 CSS/JS 版本控制的文章。 它介于您的 JS 解决方案和 August 的 svn 建议之间:http://particletree.com/notebook/automatically-version-your-css-and-javascript-files/
看起来这也可以满足您的需求,因为它是基于文件更新而不是每次访问页面来进行缓存清除,但它不需要 svn 存储库。
@Joshua McFarren:感谢您的建议。 所以我想我对这个概念理解有误。 是 **服务器** 在缓存 CSS 文件,而不是 **浏览器**? 我明白了为什么这样做有道理,我只是习惯将缓存视为浏览器的事情。
@Dan:实际上,我认为在生产网站上它甚至更重要。 即使您很少更改任何内容,但在您更改的那一天,许多访问者可能会缓存您的旧 CSS 并看到布局混乱。
@August:我喜欢版本控制的想法,即使没有 subversion 也可以做到。 但这样一来,您就需要记住要执行此操作,而不是自动完成的事情。
@Ced:像“date”这样的简单 PHP 调用真的慢得离谱吗? 我能理解可能因为这个而受到放学后的留堂,但 F?!=)
@Chris:我建议使用时间戳而不是日期的“英文”版本
style.css?4884491531235
它会从 URI 中删除空格。
获取 Firefox Web 开发者工具栏... 您可以在其中关闭缓存。 但是,我们通常不会在 Firefox 中进行大部分刷新,对吧?
服务器通常不会进行任何内容缓存,但当浏览器发送“If-Modified-Since”请求标头时,服务器可能会以“200 OK”响应新内容,否则会以“304 Not Modified”响应,并且不带响应主体,这告诉浏览器从其缓存中获取内容。
另一种选择是将该查询参数设置为文件修改的时间戳,或者其内容的哈希值
filemtime(‘path_to.css’);
md5( file_get_contents(‘path_to.css’) )
* 标签过滤器删除了我的 PHP 示例... 再次尝试。
@Joshua McFarren:您实际上 *希望* 您的脚本、CSS、图像等被访问者的浏览器缓存,否则您的服务器将为每个页面上的每个链接都受到请求,并且会严重降低响应速度。
如果您用 *页面最后修改的时间* 为每个页面添加时间戳,那么只有在任何更新后才会替换缓存 - 这肯定就是您想要的,对吧?
FWIW - 即使您的页面命名为“htm”或“html”后缀,您仍然可以在页面中使用 PHP。 请咨询您的主机,了解如何执行此操作,以及他们是否允许这样做。
很棒的文章,Chris。 我 *总是在* 向我的客户提供有关在 IE 和 FF 中刷新缓存的说明。 但是,您的技巧现在将在开发期间取消这种必要性。
最好使用 filectime(‘/path/to/file.css’) 而不是当前日期。 这将允许浏览器仅在文件实际上是新文件时才下载新文件。(filectime($file) 将返回文件上次更改的时间,以 Unix 时间戳的形式)。
因此,您的代码将变成
echo date(‘l jS \of F Y h:i:s A’, filectime(‘/path/to/style.css’));
要回声 David Walsh,我真的不明白打印以人类可读格式的日期有什么意义,时间戳就足够了
因此:echo filectime(‘/path/to/style.css’);
通过在每次页面加载时添加时间戳/日期/其他唯一标识符,您会强制刷新。 这可能比您或您的用户想要的服务器请求要多得多。
通过将其关闭,您会受到浏览器/用户的约束,由其/他们自行决定。
因此,在我看来,您想要做的是,当您将页面发布到您的新样式表时,添加您的日期技巧一小段时间。 一周? 一天? 当然,前几个新来者会遇到额外的请求... 但一旦您认为大多数用户已经获得了新的样式表,您就可以再次关闭日期标记,额外的流量就会停止。
听起来比决定何时关闭它要容易... 但这是一些折衷方案,对吧?
从另一个角度来看,如果用户在应该获得“新样式表”后又浏览了两个页面,会怎么样呢? 如果更改不仅仅是在样式表中,那么网站的外观就会乱七八糟(不仅仅是不同),绝大多数用户会看到页面完全乱七八糟,然后就会刷新页面。 如果没有乱七八糟,只是“旧的”,那么他们很快就会得到新的。
最后,我认为更重要的是,为什么不直接使用 php 的 filemtime() 函数,这样只有当 css 文件的修改日期发生变化时,请求才会发生变化呢?
@Chris
浏览器正在缓存 CSS。 不同的浏览器对缓存有不同的设置,但它通常会询问服务器(通过发送 ETag 或时间戳)是否需要再次下载 CSS 文件。 当满足某些服务器设置时,例如文件上的时间戳相同或类似情况,服务器会以 304(未修改)代码响应,这会导致您的浏览器使用缓存的副本。
您可以将服务器设置为始终响应它具有更新的文件版本,这是 Joshua 的第一个评论中提到的,但这仅适用于 Apache HTTPD。 因此,基本上,您确实需要调整一些服务器设置来永久防止缓存,但您的解决方案对于开发测试来说已经足够了。
我知道这很多,缓存还有很多其他内容,所以如果您发现我遗漏了什么,或者您还有其他问题,请告诉我。
-Eric
我已经使用了一个稍微更合理的版本。 在 html 中,我调用一个 master.css
这个 master.css @import’s (‘file-20080425.css’);
在开发过程中,我有一个 master-20080425.css 作为备用样式表,然后当我对所做的更改感到满意时,我会将它复制到当前的 master.css。 这意味着我有很多过时的样式表,但可以精确地控制当前正在调用的样式表,而不会在每次页面浏览时都进行不必要的请求。
如果您真的需要强制缓存,这将是使用 master.css?timestamp 想法的最合理方法,因为它只是一个每次都会被获取的 2-3 行文件。 仍然是一个浪费的 http 事务,但对于*每次*页面浏览来说,并不是大量的带宽。
对于 apache 指令,它不是服务器缓存。 服务器有能力提供一些额外的头信息,让浏览器知道应该多久重新获取一次此内容,因此您只是在控制服务器正在发送的提示。 客户端是否接受提示仍然由客户端决定。
我使用了一个名为 Fasterfox 的 FireFox 插件。 它简单快捷,您只需右键单击清除缓存。 然后刷新页面即可。
在地址栏中单击刷新按钮时,按住[SHIFT]键。 大多数浏览器将此识别为“强制刷新”,这意味着它们将再次从服务器获取所有文件。
在我们工作的地方,我们将 css 和图像保存在“永久”缓存的服务器上,因此我们使用版本控制系统让访问者看到修改后的文件。 我们的系统与您的 php 示例非常相似,但我们使用的是 perl。 我们在 css 文件路径中追加了一个变量,如下所示:<link rel=”stylesheet” type=”text/css” href=”style.css?v=1.0023″ /> 例如。 每次将编辑过的 css 放入生产环境时,版本号都会发生变化,从而避免访问者看到之前缓存的文件。
您也可以将 CSS 文件命名为“。php”扩展名。
这应该允许您插入以下代码以避免缓存
header(“Cache-Control: no-cache, must-revalidate”); // HTTP/1.1
header(“Expires: Mon, 26 Jul 1997 05:00:00 GMT”); // 过去的日期
@David Hucklesby
我示例中使用的 apache 指令是我们仅在发布客户审核的证明的服务器上部署的指令。 在该网站上,我们希望*确保*不会显示缓存的页面。 我无法告诉你我必须向客户解释多少次更改已经发布,他们只需要按下刷新/重新加载按钮才能看到它们。 一旦我们实现了该指令,我就再也不需要解释了。
但是您说得对,您绝对不想在整个服务器范围内随意实施该指令。 它严格用于开发,而不是部署。
嗯…
没有缓存的 CSS 毫无用处且很危险……您最好使用内联样式!
此外,您的方法不会*阻止*缓存,而是会增加缓存(每次访问页面时,都会在用户的缓存中保存一个新文件)。
使用智能版本控制。 您可以手动通过在 CSS URL 中添加一个 get 参数来实现(例如 ?version=20080505),也可以使用一些 PHP 脚本自动监控 CSS 文件的最后修改日期并将它传递给 CSS URL。
关于 css 参数
http://css-lessons.ucoz.com/css-parameters.htm
我们做了类似的事情。 我们使用的是 Oracle 和 JSP。 我创建了一个 Java 存储过程,它将文件的日期/时间戳(作为长值)追加到版本字符串中。 这样,只要文件的最后修改日期/时间发生变化,它就会自动更改。 JS 和 CSS 仍然被缓存,但是当发生更改时,客户端浏览器会请求新文件,这样用户就能看到更新后的文件。
好吧,您拯救了我,也拯救了我很多挫败感! 使用了您上面的示例,以及当前的“时间戳”,它工作得很好! 太棒了! 谢谢!
至少现在我知道我的网站将始终如一地按照预期的方式显示。
另外,要对上面提到的认为这似乎只对开发原因有益的人做出反馈 - 请参考上面的行。
当您维护一个需要不断更改的网站时,CSS 文件是最常更改的文件,当它没有自动刷新(因为我的实际页面设置为自动刷新)时,它真的很糟糕! 说真的,我曾遇到过客户来找我,说他们看不到我做的更改; 因此,这段小小的代码是我很长时间以来遇到的最棒的东西!
非常感谢!
Trace
Trace,为什么不阅读评论呢? 我的意思是,所有评论...
也适用于所有其他文件类型
ExpiresActive on
ExpiresDefault “access plus 8 hours”
ExpiresByType text/html “access plus 1 hours”
ExpiresByType text/xml “access plus 2 hours”
ExpiresByType text/css “access plus 3 hours”
ExpiresByType text/plain “access plus 4 hours”
ExpiresByType application/x-httpd-php “access plus 5 hours”
ExpiresByType application/x-javascript “access plus 6 hours”
ExpiresByType application/pdf “access plus 7 hours”
ExpiresByType image/gif “access plus 8 hours”
ExpiresByType image/png “access plus 9 hours”
ExpiresByType image/jpeg “access plus 10 hours”
ExpiresByType image/x-icon “access plus 11 hours”
这篇文章帮助我修复了我的 zend 网站问题。 我使用 ZF 开发了该网站,并使用 boot strap 进行 UI。 我上周更新了网站,但 css 并没有生效,对我来说太奇怪了,因为我也清除了浏览器的缓存,但它仍然没有生效 :(。 最后我看到了这篇文章,它帮助了我,我的问题在一分钟内就解决了。 感谢这篇很棒的文章。
大家好,希望你们都向下滚动阅读。 每个人都把这弄得*太复杂了*。 PHP 中内置了一个非常简单的函数,它允许您随时更新任何文件(在本例中是 CSS 文件),而文件名只会更改(并提示新 DL 擦除旧缓存)的时间就是您修改它的时候。 以下是代码!
<link rel="stylesheet" href="<?php bloginfo('template_url'); ?>/style.css?<?php echo filemtime(TEMPLATEPATH . '/style.css'); ?>" type="text/css" media="screen, projection" />
注意到了吗?“filemtime”将使用特定于文件最后修改时间的值对 CSS 文件进行标记。 将您的 CSS 设置为永久缓存,该文件将一直保持缓存状态,直到您修改它,这将提示新的 ID 和新的 DL 进行缓存。 我知道这是能够自由修改文件而无需担心覆盖或丢失缓存设置优势的最有效方法。
祝大家好运!(对不起版主,重新提交是因为我忘了在内联标记中放代码,它弄乱了!)
我向下滚动了。 :) 感谢您的提示 - 毫无疑问,这是在服务器端速度方面执行此操作的最佳和最有效的方法。