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

时间:2014-09-13 04:12:13

标签: javascript jquery ajax http network-protocols

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

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

Here you can see the problem,打开Fiddler或Wireshark并在点击“run”之前将其设置为记录:

<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
<div id="something" style="background-image:url(http://jsfiddle.net/img/logo-white.png);">Hello</div>
<script>

jQuery(function($) {
    $(window).load(function() {
        $.get('http://jsfiddle.net/img/logo-white.png');
    })
});

</script>

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

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

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

9 个答案:

答案 0 :(得分:6)

我使用最新的Google Chrome并提出一个请求。但是在你的JSFIDDLE例子中,你要加载jQuery两次。首先使用JSFIDDLE,然后使用脚本标记中的第二个代码。改进:JSFIDDLE

<div id="something" style="background-image:url('http://jsfiddle.net/img/logo-white.png');">Hello</div>

<script>
    jQuery(window).load(function() {
        jQuery.get('http://jsfiddle.net/img/logo-white.png');
    });

    // or

    jQuery(function($) {
        jQuery.get('http://jsfiddle.net/img/logo-white.png');
    });
</script>
当DOM准备好时调用

jQuery(function($) {...},如果DOM准备好并且加载了每个图像和其他资源,则调用jQuery(window).load(...);。将两者嵌套在一起毫无意义,请参见此处:window.onload vs $(document).ready()

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

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

答案 1 :(得分:3)

这个&#34;问题&#34;可能是一个 CORS飞行前测试

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

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

摘自MDN:

  

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

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

答案 2 :(得分:2)

您的小提琴尝试通过ajax从另一个域加载资源: cross domain request

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

<img src="smiley.png" alt="smiley" />
<div id="respText"></div>

<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
  $(window).load(function(){        
    $.get("smiley.png", function(){
      $("#respText").text("ajax request succeeded");
    });
  });
</script>

您可以测试页面here

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

火狐: Firebug

铬: chrome network panel

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

答案 3 :(得分:1)

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

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

要解决此问题,您可以将两个网址更改为/img/logo-white.png或仅{{1}}。

答案 4 :(得分:0)

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

根据, 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类型传递的。

答案 5 :(得分:0)

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

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

答案 6 :(得分:0)

  

我想要JS可访问的图像

您是否尝试 CSS using jQuery ?这很有趣 - 你有完整的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. 服务器端应用程序缓存 - 通常它会重用服务器自定义缓存,或者您创建sprite images或...
  3. 浏览器缓存 - 服务器端向图像添加缓存标头,浏览器维护缓存
  4. 如果不清楚,那么我想说清楚:您不会使用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。

    来源:How do you cache an image in Javascript

      

    在这种情况下,css和ajax通常有不同的缓存是有原因的,就好像浏览器使用不同的缓存存储来处理css和ajax请求一样吗?

    enter image description here

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

      

    使用图像预加载的一个重要原因是您要使用图像   用于mouseOver或:hover事件上元素的背景图像。   如果您只在CSS中将该背景图像应用于:hover   状态,该图像将不会加载,直到第一个:悬停事件,因此   鼠标移过它之间会有一个短暂的烦人延迟   区域和图像实际显示。

         

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

         

    在悬停时显示它的位置。

    #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,但是那个   而不是违反网络标准的精神。因此使用页面   您网页上已存在的元素。

答案 7 :(得分:0)

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

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

答案 8 :(得分:-2)

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

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

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