我注意到有关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请求使用不同的缓存存储一样?
答案 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请求,以确定实际请求是否可以安全发送。跨站点请求是这样预检的,因为它们可能对用户数据有影响。特别是,如果出现以下情况,请求会被预检:
- 它使用GET,HEAD或POST以外的方法。此外,如果使用POST来发送具有除application / x-www-form-urlencoded,multipart / form-data或text / plain之外的Content-Type的请求数据,例如,如果POST请求使用application / xml或text / xml向服务器发送XML有效负载,则该请求将被预检。
- 它在请求中设置自定义标头(例如,请求使用标头,例如X-PINGOTHER)
醇>
答案 2 :(得分:2)
您的小提琴尝试通过ajax从另一个域加载资源:
我想我创造了一个更好的例子。这是代码:
<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请求的图像来自缓存:
火狐:
铬:
所以我找不到任何意想不到的行为。
答案 3 :(得分:1)
对Ajax请求的缓存控制一直是模糊和错误的主题(example)。 跨域引用会使问题更加严重。
您提供的小提琴链接来自jsfiddle.net
,fiddle.jshell.net
是fiddle.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正在做的坏事?
很明显 浏览器错误 。
计算机是确定性的,并确切地告诉它(不希望你想要它)。如果要缓存图像,则在服务器端完成。根据谁处理缓存,可以将其处理为:
如果不清楚,那么我想说清楚:您不会使用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请求一样吗?
即使没有信息也不要妄下结论!
使用图像预加载的一个重要原因是您要使用图像 用于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;请求可能与正常情况不同,因此它会发出第二个请求,并且不会将具有不同标头的缓存值视为相同的请求。
答案 8 :(得分:-2)
浏览器将在页面上发出2个请求,导致从css调用的图像在渲染整个页面之前也使用了get请求(而不是ajax)。
窗口加载类似于de属性,并且在页面的其余部分之前加载,然后,首先请求来自Ajax的图像,而不是div上的图像,在页面加载期间处理。
如果你想在加载整个页面后加载图片,你应该使用document.ready()代替