浏览器如何处理被多次引用的不可缓存资源?

3
这篇Yahoo Developer Network文章指出,浏览器会以不同方式处理在单个HTML中被引用超过一次的不可缓存资源。我没有在HTTP/1.1 cache RFC中找到任何规定相关的规则。
我在Chrome中做了一些实验,但是无法确定确切的规则。它仅加载重复的不可缓存脚本标记一次。然后我在3个iframes中引用了相同的脚本。第一个触发了网络请求,但其他的从缓存中提供服务。我尝试引用相同URL作为图像的src,那将再次触发网络请求。
是否有关于这种行为的文档?这在不同浏览器之间有何差异?

你是如何触发无缓存状态的? - yunzen
我使用HTTP代理添加“Cache-Control: max-age=0”头。 - molnarg
也许浏览器只是看到页面的第一个请求时间,并将该确切时间用于后续调用其他文件。 - yunzen
嗯,那么 iframe 呢?它们是单独请求的,所以时间戳应该不同,但第二个和第三个 iframe 没有请求(尽管第一个已经在主页面中引用了脚本,仍然会触发请求)。 - molnarg
你能否在iframe的HTML代码中添加一个页面生成时间戳(带有微秒)作为GET变量到文件请求中?然后再看看它们是否不同? - yunzen
2个回答

3
当客户端决定检索资源时, RFC2616规定了缓存返回资源的规则,或者需要从原始服务器重新验证/重新加载资源(主要在第14.9节,但您确实需要阅读整个内容)。
然而,在同一页上有多个相同资源的副本时,根据RFC2616的规则检索第一个副本后,是否检索其他副本的决策现在由HTML5规范覆盖(主要在获取资源的处理模型中指定)。
特别是,请注意第10步:
如果资源已经因其他原因(例如,该算法的另一个调用)正在下载,并且此请求与先前请求相同(例如,具有相同的Accept和Origin标头),并且用户代理已配置为重用现有下载的数据而不是启动新的下载,则使用现有下载的结果而不是启动新的下载。
这清楚地描述了在决定是否可以重用资源时可能涉及的许多因素。一些关键点:
  • 相同的接受和来源头:大多数浏览器在任何地方都使用相同的Accept头,但在Internet Explorer中,对于图像、脚本和HTML,它们是不同的。当涉及到框架时,每个浏览器发送不同的Referer,虽然没有直接提到Referer,但只举了AcceptOrigin作为例子。

  • 已经被下载:请注意,这与已经下载完全不同。因此,如果资源在页面上出现多次,但第一次出现在第二次出现之前已经完成下载,则重用选项可能不适用。

  • 用户代理已配置为重用数据:这意味着决定重用或重新检索数据在某种程度上取决于用户代理,或者至少是一个用户选项。

最终结果是,每个浏览器处理缓存的方式略有不同。即使在特定的浏览器中,结果也可能基于时间而异。

我创建了一个包含三个嵌套帧的测试用例(即包含iframe的页面,它本身包含iframe),并复制了6个相同的脚本,每个页面上都有2个(使用Cache-Control:no-cache使它们不可缓存,但也测试了其他变化,包括max-age=0)。
  • Chrome仅加载了1个副本。
  • Internet Explorer倾向于变化,可能基于负载,但在1到3之间。
  • Safari加载了3个副本,每个帧一个(即具有不同的Referer标头)。
  • Opera和Firefox加载了所有6个副本。

当我在几个图像中重复使用相同的资源(一个在根页面上,一个在第一个iframe中)以及其他一些参考图像时,行为发生了变化。

  • Chrome现在加载了5个副本,每页都有每种类型的1个。虽然Chrome中的图像和脚本的Accept头是相同的,但头部的顺序是不同的,这表明它们可能被不同地处理,并且可能被缓存不同。
  • Internet Explorer加载了2个副本,每种类型都有1个,这是预期的。考虑到它们在只有脚本时的行为,这可能是有所不同的。
  • Safari仍然只有3个副本,每个框架一个。
  • Opera仍然是6个,原因不明。无法确定其中哪些是脚本,哪些是图像。但可能这也是可以根据负载或时间变化的东西。
  • Firefox加载了8个副本,这是预期的。6个脚本加上2个新图像。

现在,这是在正常查看页面时发生的情况 - 即只是将页面url输入地址栏。使用F5(或Safari上的等效物)强制重新加载会产生完全不同的结果。总的来说,整个重新加载的概念,F5与Ctrl-F5的区别,客户端发送的头等等在不同浏览器之间也大不相同。但这是另一天的话题。

缓存从一个浏览器到另一个浏览器非常不可预测,规范有些许留给实现者决定哪种方式最适合他们。
我希望这回答了你的问题。
额外说明:我应该提到,我没有特意测试每个浏览器的最新版本(特别是Safari是一个古老的v4,Internet Explorer是v9,但其他浏览器可能相当更新)。然而我认为这并没有太大的区别。所有浏览器突然在这方面达成一致行为的机会是极小的。

1
如果您仔细阅读了这篇文章,就会明白为什么会出现这种情况。
在Internet Explorer中,不必要的HTTP请求会发生,而在Firefox中则不会。在Internet Explorer中,如果外部脚本被包含两次且不可缓存,则在页面加载期间会生成两个HTTP请求。即使脚本是可缓存的,在用户重新加载页面时也会产生额外的HTTP请求。
这种行为只存在于Internet Explorer中。如果你问我为什么会这样,我会说IE开发人员选择忽略HTTP/1.1缓存RFC,或者至少无法实现它。也许这是一个正在进行中的工作。但再说一遍,IE在很多方面都与大多数浏览器不同(JavaScript、HTML5、CSS)。除非开发人员更新它,否则这是无法避免的。
提供的Yahoo Dev文章列出了高性能的最佳实践。这必须考虑到所有IE用户,他们受到这种影响。因此,尽管在其他浏览器中包含相同的脚本多次没有问题,但应该避免对IE用户造成伤害。

更新

不可缓存的资源会生成网络请求,无论是一次还是多次。

来自HTTP/1.1 cache RFC2. 缓存操作概述

尽管缓存是HTTP的完全可选功能,我们假设重用缓存的响应是可取的,并且当没有要求或本地所需的配置阻止它时,这种重用是默认行为。

因此,使用缓存意味着尝试重用,而不可缓存则相反。可以将不可缓存的请求视为关闭缓存的HTTP请求(回退到开启缓存的HTTP)。

Cache-Control:max-age=n不能防止缓存存储,只是声明在n秒后缓存项过期。为了防止使用缓存,请对图像使用以下标头:

Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0

谢谢您的回答。您能指出缓存RFC规定了这种行为的地方吗?我找不到它。我知道推荐的最佳实践,但我特别关注的是当您多次引用某些不可缓存的内容时浏览器的行为。正如我在问题中指出的那样,即使在符合标准的浏览器中,规则也不是非常明确,这就是为什么我要求提供有关此方面的文档参考。 - molnarg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接