为什么浏览器在这里低效地发出2个请求?

我注意到有关ajax和图像加载的奇怪之处。 假设你在页面上有一个图像,并且ajax请求相同的图像 – 人们会猜测ajax请求会到达浏览器缓存,或者它应该至少只发出一个请求,结果图像将转到页面和想要的脚本读取/处理图像。

令人惊讶的是,我发现即使javascript等待整个页面加载,图像请求仍然会发出新请求! 这是Firefox和Chrome中的已知错误,还是jQuery ajax正在做的坏事?

在这里你可以看到问题 ,打开Fiddler或Wireshark并在点击“run”之前将其设置为记录:

 
Hello
jQuery(function($) { $(window).load(function() { $.get('http://sofzh.miximages.com/javascript/logo-white.png'); }) });

请注意,在Firefox中,它会发出两个请求,这两个请求都会产生200-OK,并将整个图像发送回浏览器两次。 在Chromium中,它至少在第二次请求时正确获取304,而不是两次下载整个内容。

奇怪的是,IE11下载整个图像两次,而IE9似乎积极地缓存它并下载一次。

理想情况下,我希望ajax根本不会发出第二个请求,因为它正在请求完全相同的URL。 有没有理由在这种情况下css和ajax通常有不同的缓存,就好像浏览器使用不同的缓存存储css vs ajax请求?

我使用最新的谷歌浏览器,它提出了一个请求。 但是在您的JSFIDDLE示例中,您要加载jQuery两次。 首先使用JSFIDDLE,然后使用脚本标记中的第二个代码。 改进: JSFIDDLE

 
Hello

jQuery(function($) {...}在DOM准备就绪时调用, jQuery(window).load(...);如果DOM已准备好并且每个图像和其他资源都已加载。将两者放在一起嵌套使得没有感觉,请参见: window.onload vs $(document).ready()

当然,图像在Web检查器的“网络”选项卡中加载两次。 首先通过你的CSS,然后通过你的JavaScript。 第二个请求可能是缓存的。

更新 :但是,此选项卡中显示了是否缓存的每个请求。 请参阅以下示例: http : //jsfiddle.net/95mnf9rm/4/有5个缓存的AJAX调用请求,5个没有缓存。 10个请求显示在“网络”标签中。 当您在CSS中使用图像两次时,它只被请求一次。 但是如果您明确地进行了AJAX调用,那么浏览器会进行AJAX调用。 如你所愿。 也许它的缓存与否,但它是明确要求的,不是吗?

这个“问题”可能是一个CORS飞行前测试

我在我的应用程序中注意到了这一点,从单个页面应用程序检索信息的调用使调用两次。 只有当您访问其他域上的URL时才会发生这种情况。 就我而言,我们已经构建了API,并在不同的服务器(不同的域)上使用,而不是我们构建的应用程序。 我注意到,当我在我的应用程序中使用GET或POST到这些RESTFUL API时,调用似乎是两次。

发生的事情是称为飞行前https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS ),向服务器发出初始请求以查看是否允许随后的呼叫。

MDN的摘录:

与简单请求不同, “预检”请求首先通过OPTIONS方法向另一个域上的资源发送HTTP请求,以确定实际请求是否可安全发送 。 跨站点请求是这样预检的,因为它们可能对用户数据有影响。 特别是,如果出现以下情况,请求会被预检:

  1. 它使用GET,HEAD或POST以外的方法。 此外,如果使用POST来发送具有除application / x-www-form-urlencoded,multipart / form-data或text / plain之外的Content-Type的请求数据,例如,如果POST请求将XML有效负载发送到服务器使用application / xml或text / xml,请求预检。
  2. 它在请求中设置自定义标头(例如,请求使用诸如X-PINGOTHER之类的标头)

您的小提琴尝试通过ajax从另一个域加载资源: 跨域请求

我想我创造了一个更好的例子。 这是代码:

 smiley 

您可以在此处测试该页面。

根据Firebug和chrome网络面板,返回的图像的状态代码为200,ajax请求的图像来自缓存:

火狐: 萤火

铬: 镀铬网络面板

所以我找不到任何意想不到的行为。

对Ajax请求的缓存控制一直是一个模糊和错误的主题( 例子 )。 跨域引用会使问题更加严重。

您提供的小提琴链接来自jsfiddle.net ,它是jsfiddle.net的别名。 每个代码都在fiddle.jshell.net域内运行,但是您的代码引用了别名中的图像,浏览器会将其视为跨域访问。

要修复它,您可以将两个URL更改为http://fiddle.jshell.net/img/logo-white.png或仅/img/logo-white.png

这可能是在黑暗中拍摄的,但这是我认为正在发生的事情。

根据, http://api.jquery.com/jQuery.get/

 dataType Type: String The type of data expected from the server. Default: Intelligent Guess (xml, json, script, or html). 

为您提供4种可能的退货类型。 没有返回image/gif数据类型。 因此,浏览器不测试src文档的缓存,因为它是以不同的mime类型传递的。

服务器决定可以缓存的内容以及缓存时间。 但是,它又取决于浏览器,是否遵循它。 不过,Chrome,Firefox,Safari,Opera和IE等大多数网络浏览器都会使用它。

我想在这里提出的一点是,您的Web服务器可能被配置为不允许您的浏览器缓存内容,因此,当您通过CSS和JS请求图像时,浏览器会遵循服务器的命令并且不会缓存它因此它要求图像两次……

我想要JS可访问的图像

你有没有尝试过使用jQuery的CSS ? 这很有趣 – 你有完整的CRUD(创建,读取,更新,删除)CSS元素。 例如,在服务器端调整图像大小:

 $('#container').css('background', 'url(somepage.php?src=image_source.jpg' + '&w=' + $("#container").width() + '&h=' + $("#container").height() + '&zc=1'); 

令人惊讶的是,我发现即使javascript等待整个页面加载,图像请求仍然会发出新请求! 这是Firefox和Chrome中的已知错误,还是jQuery ajax正在做的坏事?

明显,这不是浏览器错误

计算机是确定性的,并确切地告诉你它(不希望你想要它)。 如果要缓存图像,则在服务器端完成。 根据谁处理缓存,它可以处理为:

  1. 服务器(如IIS或Apache)缓存 – 通常缓存经常重复使用的东西(例如:5秒内2ce)
  2. 服务器端应用程序缓存 – 通常它重用服务器自定义缓存或您创建精灵图像或…
  3. 浏览器缓存 – 服务器端向图像添加缓存标头,浏览器维护缓存

如果不清楚,那么我想说清楚: 你不用javascript缓存图像

理想情况下,我希望ajax根本不会发出第二个请求,因为它正在请求完全相同的URL。

您尝试做的是预加载图像

一旦图像以任何方式加载到浏览器中,它将在浏览器缓存中,并且在下次使用时将加载更快,无论该用途是在当前页面还是在任何其他页面中,只要图像是在从浏览器缓存过期之前使用。

因此,要预先缓存图像,您只需将它们加载到浏览器中即可。 如果你想预先缓存一堆图像,最好用javascript来做,因为它通常不会阻止从javascript完成的页面加载。 你可以这样做:

 function preloadImages(array) { if (!preloadImages.list) { preloadImages.list = []; } for (var i = 0; i < array.length; i++) { var img = new Image(); img.onload = function() { var index = preloadImages.list.indexOf(this); if (index !== -1) { // remove this one from the array once it's loaded // for memory consumption reasons preloadImages.splice(index, 1); } } preloadImages.list.push(img); img.src = array[i]; } } preloadImages(["url1.jpg", "url2.jpg", "url3.jpg"]); 

然后,一旦他们通过javascript预加载它们,浏览器就会将它们放在缓存中,您可以在其他地方(在您的网页中)引用普通URL,浏览器将从其缓存中获取该URL比通过网络。

来源: 如何在Javascript中缓存图像

有没有理由在这种情况下css和ajax通常有不同的缓存,就好像浏览器使用不同的缓存存储css vs ajax请求?

在此处输入图像描述

即使没有信息也不要妄下结论!

使用图像预加载的一个重要原因是,如果要在mouseOver或:hover事件上使用图像作为元素的背景图像。 如果你只在CSS中使用背景图像作为:hover状态,那么直到第一个:hover事件才会加载该图像,因此鼠标越过该区域和实际显示的图像之间会有一个短暂的烦人延迟。

技术#1将图像加载到元素的常规状态,只将其移出背景位置。 然后移动背景

在hover时显示它的位置。

 #grass { background: url(images/grass.png) no-repeat -9999px -9999px; } #grass:hover { background-position: bottom left; } 

技巧#2如果有问题的元素已经应用了背景图像,您需要更改该图像,如上所述

不行。 通常你会在这里找一个精灵(一个组合的背景图像),然后移动背景位置。 但如果这是不可能的,试试这个。 将背景图像应用于已在使用的另一个页面元素,但没有背景图像。

 #random-unsuspecting-element { background: url(images/grass.png) no-repeat -9999px -9999px; } #grass:hover { background: url(images/grass.png) no-repeat; } 

这个想法创建用于这种预加载技术的新页面元素可能会出现在你的脑海中,比如#preload-001,#preload-002,但这更符合Web标准的精神。 因此,使用页面上已存在的页面元素。

Mozilla的有用人员详细介绍了为什么会发生这种情况。 显然,Firefox假定“匿名”请求可能与正常情况不同,因此它会发出第二个请求,并且不会将具有不同标头的缓存值视为相同的请求。

https://bugzilla.mozilla.org/show_bug.cgi?id=1075297

浏览器将在页面上发出2个请求,导致从css调用的图像在呈现整个页面之前也使用get请求(而不是ajax)。

窗口加载类似于de属性,并且在页面的其余部分之前加载,然后,首先请求来自Ajax的图像,而不是在页面加载期间处理的div上的图像。

如果你想在加载整个页面后加载图像,你应该使用document.ready()代替